mirror of
https://github.com/tuneinsight/lattigo.git
synced 2025-09-13 03:27:14 +00:00
Initial commit
This commit is contained in:
49
.gitignore
vendored
Normal file
49
.gitignore
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
# Created by .ignore support plugin (hsz.mobi)
|
||||
### Go template
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
||||
.glide/
|
||||
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.prof
|
||||
*.iml
|
||||
*.cov
|
||||
*.gob
|
||||
|
||||
.DS_Store
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
.idea/
|
||||
13
.travis.yml
Normal file
13
.travis.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.x
|
||||
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
|
||||
script:
|
||||
- env GO111MODULE=on make test
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
177
LICENSE
Normal file
177
LICENSE
Normal file
@@ -0,0 +1,177 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"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.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"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.
|
||||
|
||||
"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).
|
||||
|
||||
"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.
|
||||
|
||||
"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."
|
||||
|
||||
"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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; 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) 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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
34
Makefile
Normal file
34
Makefile
Normal file
@@ -0,0 +1,34 @@
|
||||
EXCLUDE_LINT = "_test.go"
|
||||
|
||||
test_fmt:
|
||||
@echo Checking correct formatting of files
|
||||
@{ \
|
||||
files=$$( go fmt ./... ); \
|
||||
if [ -n "$$files" ]; then \
|
||||
echo "Files not properly formatted: $$files"; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
if ! go vet ./...; then \
|
||||
exit 1; \
|
||||
fi \
|
||||
}
|
||||
|
||||
test_lint:
|
||||
@echo Checking linting of files
|
||||
@{ \
|
||||
go install golang.org/x/lint/golint; \
|
||||
el=$(EXCLUDE_LINT); \
|
||||
lintfiles=$$( golint ./... | egrep -v "$$el" ); \
|
||||
if [ -n "$$lintfiles" ]; then \
|
||||
echo "Lint errors:"; \
|
||||
echo "$$lintfiles"; \
|
||||
exit 1; \
|
||||
fi \
|
||||
}
|
||||
|
||||
test_local:
|
||||
go test -v -race -short -p=1 ./...
|
||||
|
||||
test: test_fmt test_local
|
||||
|
||||
local: test_fmt test_lint test_local
|
||||
13
NOTICE
Normal file
13
NOTICE
Normal file
@@ -0,0 +1,13 @@
|
||||
Copyright 2019 EPFL
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
38
README.md
Normal file
38
README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# LattiGo: Lattice Cryptography Library in Golang
|
||||
[](https://travis-ci.com/lca1/lattigo)
|
||||
|
||||
[](https://travis-ci.com/lca1/lattigo)
|
||||
|
||||
This package provides a toolbox of lattice-based cryptographic primitives for Go. The library is still at an experimental stage and should be used for research purposes only.
|
||||
|
||||
The __LattiGo__ subpackages from the lowest to the highest abstraction level and their provided functionalities are as follows:
|
||||
|
||||
|
||||
|
||||
- `Ring`: CRT accelerated modular arithmetic operations for polynomials, including CRT basis extension, CRT rescaling, Number Theoretic Transformation (NTT), uniform, Gaussian and Ternary sampling.
|
||||
- Post-quantum key exchange - a new hope (<https://eprint.iacr.org/2015/1092>)
|
||||
- Faster arithmetic for number-theoretic transforms (<https://arxiv.org/abs/1205.2926>)
|
||||
- Speeding up the Number Theoretic Transform for Faster Ideal Lattice-Based Cryptography (<https://eprint.iacr.org/2016/504>)
|
||||
- Gaussian sampling in lattice-based cryptography (<https://tel.archives-ouvertes.fr/tel-01245066v2>)
|
||||
|
||||
- `BFV`: CRT accellerated Fan-Vercauteren variant of Brakerski's scale invariant homomorphic encryption scheme. Provides modular arithmetic over the integers.
|
||||
- Somewhat Practical Fully Homomorphic Encryption (<https://eprint.iacr.org/2012/144>).
|
||||
- A Full RNS Variant of FV Like Somewhat Homomorphic Encryption Schemes (<https://eprint.iacr.org/2016/510>)
|
||||
- An Improved RNS Variant of the BFV Homomorphic Encryption Scheme (<https://eprint.iacr.org/2018/117>)
|
||||
|
||||
- `CKKS`: CRT accelerated version of the Homomorphic Encryption for Arithmetic for Approximate Numbers scheme. Provides approximate arithmetic over the complex numbers.
|
||||
- Homomorphic Encryption for Arithmetic of Approximate Numbers (<https://eprint.iacr.org/2016/421>)
|
||||
- A Full RNS Variant of Approximate Homomorphic Encryption (<https://eprint.iacr.org/2018/931>)
|
||||
- Improved Bootstrapping for Approximate Homomorphic Encryption (<https://eprint.iacr.org/2018/1043>)
|
||||
|
||||
- `Distributed BFV/CKKS`: Distributed version of the BFV and CKKS. Provides protocols for the generation of shared private and public keys, and switching keys (evaluation keys, switching keys and rotation keys), as well as a protocol to operate the key-switching on a shared ciphertext.
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
In the subfolder "examples" you can find runnable go files giving examples on the usage of the library.
|
||||
In each subpackage you can find additional test files documenting further usage approaches.
|
||||
|
||||
## License
|
||||
|
||||
__LattiGo__ is licenced under the Apache License version 2.0.
|
||||
186
bfv/bfv.go
Normal file
186
bfv/bfv.go
Normal file
@@ -0,0 +1,186 @@
|
||||
package bfv
|
||||
|
||||
import (
|
||||
"github.com/lca1/lattigo/ring"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
type BfvContext struct {
|
||||
|
||||
// Polynomial degree
|
||||
n uint64
|
||||
|
||||
// Plaintext Modulus
|
||||
t uint64
|
||||
|
||||
// floor(Q/t) mod each Qi in montgomeryform
|
||||
DeltaMont []uint64
|
||||
Delta []uint64
|
||||
|
||||
// Ternary and Gaussian samplers
|
||||
sigma float64
|
||||
gaussianSampler *ring.KYSampler
|
||||
ternarySampler *ring.TernarySampler
|
||||
|
||||
// Maximum bitLength of the modulies
|
||||
maxBit uint64
|
||||
|
||||
// Polynomial contexts
|
||||
contextT *ring.Context
|
||||
contextQ *ring.Context
|
||||
contextP *ring.Context
|
||||
contextQP *ring.Context
|
||||
|
||||
// Galois elements used to permute the batched plaintext in the encrypted domain
|
||||
gen uint64
|
||||
genInv uint64
|
||||
|
||||
galElRotRow uint64
|
||||
galElRotColLeft []uint64
|
||||
galElRotColRight []uint64
|
||||
|
||||
// Checksum of [n, [modulies]]
|
||||
checksum []byte
|
||||
}
|
||||
|
||||
func NewBfvContext() *BfvContext {
|
||||
return new(BfvContext)
|
||||
}
|
||||
|
||||
func NewBfvContextWithParam(N, t uint64, ModulieQ, ModulieP []uint64, sigma float64) (newbfvcontext *BfvContext, err error) {
|
||||
newbfvcontext = new(BfvContext)
|
||||
if err := newbfvcontext.SetParameters(N, t, ModulieQ, ModulieP, sigma); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (bfvContext *BfvContext) SetParameters(N, t uint64, ModulieQ, ModulieP []uint64, sigma float64) (err error) {
|
||||
|
||||
contextT := ring.NewContext()
|
||||
contextQ := ring.NewContext()
|
||||
contextP := ring.NewContext()
|
||||
contextQP := ring.NewContext()
|
||||
|
||||
// Plaintext NTT Parameters
|
||||
// We do not check for an error since the plaintext NTT is optional
|
||||
// it will still compute the other relevant parameters
|
||||
contextT.SetParameters(N, []uint64{t})
|
||||
contextT.ValidateParameters()
|
||||
// ========================
|
||||
|
||||
if err := contextQ.SetParameters(N, ModulieQ); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := contextQ.ValidateParameters(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := contextP.SetParameters(N, ModulieP); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := contextP.ValidateParameters(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := contextQP.Merge(contextQ, contextP); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bfvContext.n = N
|
||||
|
||||
bfvContext.t = t
|
||||
|
||||
delta := ring.NewUint(1).Div(contextQ.ModulusBigint, ring.NewUint(t))
|
||||
tmpBig := ring.NewUint(1)
|
||||
bfvContext.DeltaMont = make([]uint64, len(ModulieQ))
|
||||
bfvContext.Delta = make([]uint64, len(ModulieQ))
|
||||
for i, Qi := range ModulieQ {
|
||||
bfvContext.Delta[i] = tmpBig.Mod(delta, ring.NewUint(Qi)).Uint64()
|
||||
bfvContext.DeltaMont[i] = ring.MForm(bfvContext.Delta[i], Qi, contextQ.GetBredParams()[i])
|
||||
}
|
||||
|
||||
bfvContext.sigma = sigma
|
||||
|
||||
bfvContext.gaussianSampler = contextQ.NewKYSampler(sigma, int(6*sigma))
|
||||
bfvContext.ternarySampler = contextQ.NewTernarySampler()
|
||||
|
||||
bfvContext.maxBit = 0
|
||||
|
||||
for i := range ModulieQ {
|
||||
tmp := uint64(bits.Len64(ModulieQ[i]))
|
||||
if tmp > bfvContext.maxBit {
|
||||
bfvContext.maxBit = tmp
|
||||
}
|
||||
}
|
||||
|
||||
bfvContext.contextT = contextT
|
||||
bfvContext.contextQ = contextQ
|
||||
bfvContext.contextP = contextP
|
||||
bfvContext.contextQP = contextQP
|
||||
|
||||
bfvContext.gen = 5
|
||||
bfvContext.genInv = modexp(bfvContext.gen, (N<<1)-1, N<<1)
|
||||
|
||||
mask := (N << 1) - 1
|
||||
|
||||
bfvContext.galElRotColLeft = make([]uint64, N>>1)
|
||||
bfvContext.galElRotColRight = make([]uint64, N>>1)
|
||||
|
||||
bfvContext.galElRotColRight[0] = 1
|
||||
bfvContext.galElRotColLeft[0] = 1
|
||||
|
||||
for i := uint64(1); i < N>>1; i++ {
|
||||
bfvContext.galElRotColLeft[i] = (bfvContext.galElRotColLeft[i-1] * bfvContext.gen) & mask
|
||||
bfvContext.galElRotColRight[i] = (bfvContext.galElRotColRight[i-1] * bfvContext.genInv) & mask
|
||||
|
||||
}
|
||||
|
||||
bfvContext.galElRotRow = (N << 1) - 1
|
||||
|
||||
toHash := make([]uint64, len(ModulieQ)+1)
|
||||
toHash[0] = N
|
||||
for i := 1; i < len(toHash); i++ {
|
||||
toHash[i] = ModulieQ[i-1]
|
||||
}
|
||||
|
||||
if bfvContext.checksum, err = Hash(toHash); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bfvContext *BfvContext) GetN() uint64 {
|
||||
return bfvContext.n
|
||||
}
|
||||
|
||||
func (bfvContext *BfvContext) GetPlaintextModulus() uint64 {
|
||||
return bfvContext.t
|
||||
}
|
||||
|
||||
func (bfvContext *BfvContext) GetDelta() []uint64 {
|
||||
return bfvContext.DeltaMont
|
||||
}
|
||||
|
||||
func (bfvContext *BfvContext) GetSigma() float64 {
|
||||
return bfvContext.sigma
|
||||
}
|
||||
|
||||
func (bfvContext *BfvContext) GetContextT() *ring.Context {
|
||||
return bfvContext.contextT
|
||||
}
|
||||
|
||||
func (bfvContext *BfvContext) GetContextQ() *ring.Context {
|
||||
return bfvContext.contextQ
|
||||
}
|
||||
|
||||
func (bfvContext *BfvContext) GetContextP() *ring.Context {
|
||||
return bfvContext.contextP
|
||||
}
|
||||
|
||||
func (bfvContext *BfvContext) GetContextQP() *ring.Context {
|
||||
return bfvContext.contextQP
|
||||
}
|
||||
163
bfv/bfv_benchmark_test.go
Executable file
163
bfv/bfv_benchmark_test.go
Executable file
@@ -0,0 +1,163 @@
|
||||
package bfv
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkBFVScheme(b *testing.B) {
|
||||
|
||||
paramSets := ParamSets60
|
||||
|
||||
bitDecomps := []uint64{60}
|
||||
|
||||
for _, params := range paramSets {
|
||||
|
||||
bfvContext := NewBfvContext()
|
||||
if err := bfvContext.SetParameters(params.N, params.T, params.Qi, params.Pi, params.Sigma); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
var sk *SecretKey
|
||||
var pk *PublicKey
|
||||
var err error
|
||||
|
||||
kgen := bfvContext.NewKeyGenerator()
|
||||
|
||||
// Public Key Generation
|
||||
b.Run(fmt.Sprintf("params=%d/KeyGen", params.N), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sk, pk, err = kgen.NewKeyPair()
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Encryption
|
||||
encryptor, err := bfvContext.NewEncryptor(pk)
|
||||
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
ptcoeffs := bfvContext.NewRandomPlaintextCoeffs()
|
||||
pt := bfvContext.NewPlaintext()
|
||||
pt.SetCoefficientsUint64(ptcoeffs)
|
||||
b.Run(fmt.Sprintf("params=%d/EncryptNew", params.N), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = encryptor.EncryptNew(pt)
|
||||
}
|
||||
})
|
||||
|
||||
ctd1 := bfvContext.NewCiphertext(1)
|
||||
b.Run(fmt.Sprintf("params=%d/Encrypt", params.N), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = encryptor.Encrypt(pt, ctd1)
|
||||
}
|
||||
})
|
||||
|
||||
// Decryption
|
||||
decryptor, err := bfvContext.NewDecryptor(sk, 1)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
ptp := bfvContext.NewPlaintext()
|
||||
b.Run(fmt.Sprintf("params=%d/Decrypt", params.N), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
decryptor.Decrypt(ctd1, ptp)
|
||||
}
|
||||
_ = ptp
|
||||
})
|
||||
|
||||
evaluator, err := bfvContext.NewEvaluator()
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
ct1, err := encryptor.EncryptNew(pt)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
ct2, err := encryptor.EncryptNew(pt)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
// Addition
|
||||
b.Run(fmt.Sprintf("params=%d/Add", params.N), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err := evaluator.Add(ct1, ct2, ctd1); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Subtraction
|
||||
b.Run(fmt.Sprintf("params=%d/Sub", params.N), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err := evaluator.Sub(ct1, ct2, ctd1); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Multiplication
|
||||
ctd2 := bfvContext.NewCiphertext(2)
|
||||
b.Run(fmt.Sprintf("params=%d/Multiply", params.N), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = evaluator.Mul(ct1, ct2, ctd2) //The receiver needs to be in QP, and will be returned in Q.
|
||||
}
|
||||
})
|
||||
|
||||
// Square is Mul(ct, ct) for now
|
||||
b.Run(fmt.Sprintf("params=%d/Square", params.N), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = evaluator.Mul(ct1, ct1, ctd2)
|
||||
}
|
||||
})
|
||||
|
||||
for _, bitDecomp := range bitDecomps {
|
||||
|
||||
// Relinearization Key Generation not becnhmarked (no inplace gen)
|
||||
rlk, err := kgen.NewRelinKey(sk, 2, bitDecomp)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
// Relinearization
|
||||
b.Run(fmt.Sprintf("params=%d/decomp=%d/Relin", params.N, bitDecomp), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err := evaluator.Relinearize(ctd2, rlk, ctd1); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Rotation Key Generation not benchmarked (no inplace gen)
|
||||
rtk, err := kgen.NewRotationKeysPow2(sk, bitDecomp, true)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
// Rotation Rows
|
||||
b.Run(fmt.Sprintf("params=%d/decomp=%d/RotateRows", params.N, bitDecomp), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err := evaluator.RotateRows(ct1, rtk, ctd1); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Rotation Cols
|
||||
b.Run(fmt.Sprintf("params=%d/decomp=%d/RotateCols", params.N, bitDecomp), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err := evaluator.RotateColumns(ct1, 1, rtk, ctd1); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
1131
bfv/bfv_test.go
Normal file
1131
bfv/bfv_test.go
Normal file
File diff suppressed because it is too large
Load Diff
25
bfv/bigpoly.go
Normal file
25
bfv/bigpoly.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package bfv
|
||||
|
||||
import (
|
||||
"github.com/lca1/lattigo/ring"
|
||||
)
|
||||
|
||||
type BigPoly struct {
|
||||
value []*ring.Poly
|
||||
bfvcontext *BfvContext
|
||||
isNTT bool
|
||||
}
|
||||
|
||||
type BfvElement interface {
|
||||
Value() []*ring.Poly
|
||||
SetValue([]*ring.Poly)
|
||||
BfvContext() *BfvContext
|
||||
Resize(uint64)
|
||||
CopyNew() BfvElement
|
||||
Copy(BfvElement) error
|
||||
Degree() uint64
|
||||
NTT(BfvElement) error
|
||||
InvNTT(BfvElement) error
|
||||
IsNTT() bool
|
||||
SetIsNTT(bool)
|
||||
}
|
||||
138
bfv/ciphertext.go
Normal file
138
bfv/ciphertext.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package bfv
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
)
|
||||
|
||||
type Ciphertext BigPoly
|
||||
|
||||
// NewCiphertext creates a new ciphertext of degree n
|
||||
func (bfvcontext *BfvContext) NewCiphertext(degree uint64) *Ciphertext {
|
||||
ciphertext := new(Ciphertext)
|
||||
ciphertext.bfvcontext = bfvcontext
|
||||
ciphertext.value = make([]*ring.Poly, degree+1)
|
||||
for i := uint64(0); i < degree+1; i++ {
|
||||
ciphertext.value[i] = bfvcontext.contextQ.NewPoly()
|
||||
}
|
||||
ciphertext.isNTT = false
|
||||
|
||||
return ciphertext
|
||||
}
|
||||
|
||||
// NewCiphertext creates a new ciphertext of degree n
|
||||
func (bfvcontext *BfvContext) NewCiphertextBig(degree uint64) *Ciphertext {
|
||||
ciphertext := new(Ciphertext)
|
||||
ciphertext.bfvcontext = bfvcontext
|
||||
|
||||
ciphertext.value = make([]*ring.Poly, degree+1)
|
||||
for i := uint64(0); i < degree+1; i++ {
|
||||
ciphertext.value[i] = bfvcontext.contextQP.NewPoly()
|
||||
}
|
||||
ciphertext.isNTT = false
|
||||
|
||||
return ciphertext
|
||||
}
|
||||
|
||||
func (bfvcontext *BfvContext) NewRandomCiphertext(degree uint64) *Ciphertext {
|
||||
ciphertext := new(Ciphertext)
|
||||
ciphertext.bfvcontext = bfvcontext
|
||||
|
||||
ciphertext.value = make([]*ring.Poly, degree+1)
|
||||
for i := uint64(0); i < degree+1; i++ {
|
||||
ciphertext.value[i] = bfvcontext.contextQ.NewUniformPoly()
|
||||
}
|
||||
ciphertext.isNTT = false
|
||||
|
||||
return ciphertext
|
||||
}
|
||||
|
||||
func (ctx *Ciphertext) BfvContext() *BfvContext {
|
||||
return ctx.bfvcontext
|
||||
}
|
||||
|
||||
func (ctx *Ciphertext) Value() []*ring.Poly {
|
||||
return ctx.value
|
||||
}
|
||||
|
||||
func (ctx *Ciphertext) SetValue(value []*ring.Poly) {
|
||||
ctx.value = value
|
||||
}
|
||||
|
||||
func (ctx *Ciphertext) Degree() uint64 {
|
||||
return uint64(len(ctx.value) - 1)
|
||||
}
|
||||
|
||||
func (ctx *Ciphertext) Resize(degree uint64) {
|
||||
if ctx.Degree() > degree {
|
||||
ctx.value = ctx.value[:degree]
|
||||
} else if ctx.Degree() < degree {
|
||||
for ctx.Degree() < degree {
|
||||
ctx.value = append(ctx.value, []*ring.Poly{ctx.bfvcontext.contextQ.NewPoly()}...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ctx *Ciphertext) IsNTT() bool {
|
||||
return ctx.isNTT
|
||||
}
|
||||
|
||||
func (ctx *Ciphertext) SetIsNTT(value bool) {
|
||||
ctx.isNTT = value
|
||||
}
|
||||
|
||||
// Creates a new ciphertext of the same format and coefficients.
|
||||
func (ctx *Ciphertext) CopyNew() BfvElement {
|
||||
|
||||
ctxCopy := new(Ciphertext)
|
||||
|
||||
ctxCopy.value = make([]*ring.Poly, ctx.Degree()+1)
|
||||
for i := range ctx.value {
|
||||
ctxCopy.value[i] = ctx.value[i].CopyNew()
|
||||
}
|
||||
ctxCopy.bfvcontext = ctx.bfvcontext
|
||||
ctxCopy.isNTT = ctx.isNTT
|
||||
|
||||
return ctxCopy
|
||||
}
|
||||
|
||||
// Copies the value of the ciphertext on a reciever ciphertext of the same format
|
||||
func (ctx *Ciphertext) Copy(ctxCopy BfvElement) error {
|
||||
|
||||
if !checkContext([]BfvElement{ctx, ctxCopy}) {
|
||||
return errors.New("input ciphertext are not using the same bfvcontext")
|
||||
}
|
||||
|
||||
for i := range ctxCopy.Value() {
|
||||
ctxCopy.Value()[i].Copy(ctx.Value()[i])
|
||||
}
|
||||
ctxCopy.SetIsNTT(ctx.IsNTT())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ctx *Ciphertext) NTT(c BfvElement) error {
|
||||
if ctx.Degree() != c.Degree() {
|
||||
return errors.New("error : receiver element invalide degree (does not match)")
|
||||
}
|
||||
if ctx.IsNTT() != true {
|
||||
for i := range ctx.value {
|
||||
ctx.bfvcontext.contextQ.NTT(ctx.Value()[i], c.Value()[i])
|
||||
}
|
||||
c.SetIsNTT(true)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ctx *Ciphertext) InvNTT(c BfvElement) error {
|
||||
if ctx.Degree() != c.Degree() {
|
||||
return errors.New("error : receiver element invalide degree (does not match)")
|
||||
}
|
||||
if ctx.IsNTT() != false {
|
||||
for i := range ctx.value {
|
||||
ctx.bfvcontext.contextQ.InvNTT(ctx.Value()[i], c.Value()[i])
|
||||
}
|
||||
c.SetIsNTT(false)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
63
bfv/decryptor.go
Normal file
63
bfv/decryptor.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package bfv
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
)
|
||||
|
||||
type Decryptor struct {
|
||||
bfvcontext *BfvContext
|
||||
sk *SecretKey
|
||||
polypool *ring.Poly
|
||||
}
|
||||
|
||||
func (bfvcontext *BfvContext) NewDecryptor(sk *SecretKey, maxDegree int) (*Decryptor, error) {
|
||||
|
||||
if sk.sk.GetDegree() != int(bfvcontext.n) {
|
||||
return nil, errors.New("error : secret_key degree must match context degree")
|
||||
}
|
||||
|
||||
decryptor := new(Decryptor)
|
||||
|
||||
decryptor.bfvcontext = bfvcontext
|
||||
|
||||
decryptor.sk = sk
|
||||
|
||||
decryptor.polypool = bfvcontext.contextQ.NewPoly()
|
||||
|
||||
return decryptor, nil
|
||||
}
|
||||
|
||||
func (decryptor *Decryptor) DecryptNew(ciphertext *Ciphertext) (*Plaintext, error) {
|
||||
|
||||
plaintext := decryptor.bfvcontext.NewPlaintext()
|
||||
|
||||
if err := decryptor.Decrypt(ciphertext, plaintext); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
func (decryptor *Decryptor) Decrypt(ciphertext *Ciphertext, plaintext *Plaintext) (err error) {
|
||||
|
||||
decryptor.bfvcontext.contextQ.NTT(ciphertext.value[ciphertext.Degree()], plaintext.value[0])
|
||||
|
||||
for i := uint64(ciphertext.Degree()); i > 0; i-- {
|
||||
decryptor.bfvcontext.contextQ.MulCoeffsMontgomery(plaintext.value[0], decryptor.sk.sk, plaintext.value[0])
|
||||
decryptor.bfvcontext.contextQ.NTT(ciphertext.value[i-1], decryptor.polypool)
|
||||
decryptor.bfvcontext.contextQ.Add(plaintext.value[0], decryptor.polypool, plaintext.value[0])
|
||||
|
||||
if i&7 == 7 {
|
||||
decryptor.bfvcontext.contextQ.Reduce(plaintext.value[0], plaintext.value[0])
|
||||
}
|
||||
}
|
||||
|
||||
if (ciphertext.Degree())&7 != 7 {
|
||||
decryptor.bfvcontext.contextQ.Reduce(plaintext.value[0], plaintext.value[0])
|
||||
}
|
||||
|
||||
decryptor.bfvcontext.contextQ.InvNTT(plaintext.value[0], plaintext.value[0])
|
||||
|
||||
return nil
|
||||
}
|
||||
239
bfv/encoder.go
Normal file
239
bfv/encoder.go
Normal file
@@ -0,0 +1,239 @@
|
||||
package bfv
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
type BatchEncoder struct {
|
||||
indexMatrix []uint64
|
||||
bfvcontext *BfvContext
|
||||
simplescaler *ring.SimpleScaler
|
||||
}
|
||||
|
||||
func (bfvcontext *BfvContext) NewBatchEncoder() *BatchEncoder {
|
||||
|
||||
var m, gen, pos, index1, index2 uint64
|
||||
|
||||
batchencoder := new(BatchEncoder)
|
||||
|
||||
batchencoder.bfvcontext = bfvcontext
|
||||
|
||||
slots := bfvcontext.n
|
||||
|
||||
batchencoder.indexMatrix = make([]uint64, slots)
|
||||
|
||||
logN := uint64(bits.Len64(bfvcontext.n) - 1)
|
||||
|
||||
row_size := bfvcontext.n >> 1
|
||||
m = (bfvcontext.n << 1)
|
||||
gen = bfvcontext.gen
|
||||
pos = 1
|
||||
|
||||
for i := uint64(0); i < row_size; i++ {
|
||||
|
||||
index1 = (pos - 1) >> 1
|
||||
index2 = (m - pos - 1) >> 1
|
||||
|
||||
batchencoder.indexMatrix[i] = bitReverse64(index1, logN)
|
||||
batchencoder.indexMatrix[i|row_size] = bitReverse64(index2, logN)
|
||||
|
||||
pos *= gen
|
||||
pos &= (m - 1)
|
||||
}
|
||||
|
||||
batchencoder.simplescaler, _ = ring.NewSimpleScaler(bfvcontext.t, bfvcontext.contextQ)
|
||||
|
||||
return batchencoder
|
||||
}
|
||||
|
||||
func (batchencoder *BatchEncoder) EncodeUint(coeffs []uint64, plaintext *Plaintext) error {
|
||||
|
||||
if len(coeffs) > len(batchencoder.indexMatrix) {
|
||||
return errors.New("error : invalid input to encode (number of coefficients must be smaller or equal to the context)")
|
||||
}
|
||||
|
||||
if len(plaintext.value[0].Coeffs[0]) != len(batchencoder.indexMatrix) {
|
||||
return errors.New("error : invalid plaintext to receive encoding (number of coefficients does not match the context of the encoder")
|
||||
}
|
||||
|
||||
for i := 0; i < len(coeffs); i++ {
|
||||
plaintext.value[0].Coeffs[0][batchencoder.indexMatrix[i]] = coeffs[i]
|
||||
}
|
||||
|
||||
for i := len(coeffs); i < len(batchencoder.indexMatrix); i++ {
|
||||
plaintext.value[0].Coeffs[0][batchencoder.indexMatrix[i]] = 0
|
||||
}
|
||||
|
||||
if err := plaintext.EMB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
plaintext.Lift()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (batchencoder *BatchEncoder) EncodeInt(coeffs []int64, plaintext *Plaintext) error {
|
||||
|
||||
if len(coeffs) > len(batchencoder.indexMatrix) {
|
||||
return errors.New("error : invalid input to encode (number of coefficients must be smaller or equal to the context)")
|
||||
}
|
||||
|
||||
if len(plaintext.value[0].Coeffs[0]) != len(batchencoder.indexMatrix) {
|
||||
return errors.New("error : invalid plaintext to receive encoding (number of coefficients does not match the context of the encoder")
|
||||
}
|
||||
|
||||
for i := 0; i < len(coeffs); i++ {
|
||||
|
||||
if coeffs[i] < 0 {
|
||||
plaintext.value[0].Coeffs[0][batchencoder.indexMatrix[i]] = uint64(int64(plaintext.bfvcontext.t) + coeffs[i])
|
||||
} else {
|
||||
plaintext.value[0].Coeffs[0][batchencoder.indexMatrix[i]] = uint64(coeffs[i])
|
||||
}
|
||||
}
|
||||
|
||||
for i := len(coeffs); i < len(batchencoder.indexMatrix); i++ {
|
||||
plaintext.value[0].Coeffs[0][batchencoder.indexMatrix[i]] = 0
|
||||
}
|
||||
|
||||
if err := plaintext.EMB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
plaintext.Lift()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (batchencoder *BatchEncoder) DecodeUint(plaintext *Plaintext) ([]uint64, error) {
|
||||
|
||||
if len(plaintext.value[0].Coeffs[0]) != len(batchencoder.indexMatrix) {
|
||||
return nil, errors.New("error : invalid plaintext to decode (number of coefficients does not match the context of the encoder")
|
||||
}
|
||||
|
||||
tmp := batchencoder.bfvcontext.contextQ.NewPoly()
|
||||
|
||||
batchencoder.simplescaler.Scale(plaintext.value[0], tmp)
|
||||
|
||||
batchencoder.bfvcontext.contextT.NTT(tmp, tmp)
|
||||
|
||||
coeffs := make([]uint64, batchencoder.bfvcontext.n)
|
||||
|
||||
for i := uint64(0); i < batchencoder.bfvcontext.n; i++ {
|
||||
coeffs[i] = tmp.Coeffs[0][batchencoder.indexMatrix[i]]
|
||||
}
|
||||
|
||||
return coeffs, nil
|
||||
|
||||
}
|
||||
|
||||
func (batchencoder *BatchEncoder) DecodeInt(plaintext *Plaintext) ([]int64, error) {
|
||||
|
||||
var value int64
|
||||
|
||||
if len(plaintext.value[0].Coeffs[0]) != len(batchencoder.indexMatrix) {
|
||||
return nil, errors.New("error : invalid plaintext to decode (number of coefficients does not match the context of the encoder")
|
||||
}
|
||||
|
||||
tmp := batchencoder.bfvcontext.contextQ.NewPoly()
|
||||
|
||||
batchencoder.simplescaler.Scale(plaintext.value[0], tmp)
|
||||
|
||||
batchencoder.bfvcontext.contextT.NTT(tmp, tmp)
|
||||
|
||||
coeffs := make([]int64, batchencoder.bfvcontext.n)
|
||||
|
||||
modulus := int64(batchencoder.bfvcontext.t)
|
||||
|
||||
for i := uint64(0); i < batchencoder.bfvcontext.n; i++ {
|
||||
|
||||
value = int64(tmp.Coeffs[0][batchencoder.indexMatrix[i]])
|
||||
|
||||
coeffs[i] = value
|
||||
|
||||
if value > modulus>>1 {
|
||||
coeffs[i] -= modulus
|
||||
}
|
||||
}
|
||||
|
||||
return coeffs, nil
|
||||
|
||||
}
|
||||
|
||||
type IntEncoder struct {
|
||||
base int64
|
||||
simplescaler *ring.SimpleScaler
|
||||
}
|
||||
|
||||
func (bfvcontext *BfvContext) NewIntEncoder(base int64) *IntEncoder {
|
||||
encoder := new(IntEncoder)
|
||||
encoder.base = base
|
||||
encoder.simplescaler, _ = ring.NewSimpleScaler(bfvcontext.t, bfvcontext.contextQ)
|
||||
return encoder
|
||||
}
|
||||
|
||||
func (encoder *IntEncoder) Encode(msg int64, plaintext *Plaintext) {
|
||||
plaintext.value[0].Coeffs[0] = intEncode(msg, encoder.base, int64(plaintext.bfvcontext.t), plaintext.value[0].Coeffs[0])
|
||||
plaintext.Lift()
|
||||
}
|
||||
|
||||
func (encoder *IntEncoder) Decode(plaintext *Plaintext) int64 {
|
||||
tmp := plaintext.bfvcontext.contextQ.NewPoly()
|
||||
encoder.simplescaler.Scale(plaintext.value[0], tmp)
|
||||
return intDecode(tmp.Coeffs[0], encoder.base, int64(plaintext.bfvcontext.t))
|
||||
}
|
||||
|
||||
// Encodes an integer on a ring F given a base W.
|
||||
// Decomposes the integer in base W, then set the coefficients
|
||||
// of F accordingly.
|
||||
// One has to be carefule about two things :
|
||||
// - Make sure that base > T/2 (or it might not decode properly)
|
||||
// - Make sure that log_base(msg) <= N (or it will not fit in the ring)
|
||||
func intEncode(msg, base, modulus int64, coeffs []uint64) []uint64 {
|
||||
|
||||
//if base > plaintext.Context.Qi[0]>>1{
|
||||
// return errors.New("Encoding base > T/2")
|
||||
//}
|
||||
//if msg != 0{
|
||||
// msgLen := uint64(math.Round(math.Log(math.Abs(float64(msg)))/math.Log(float64(base))))
|
||||
//}
|
||||
//if msgLen > plaintext.Context.N{
|
||||
// return errors.New("Messages does not fit on the ring with the given base")
|
||||
//}
|
||||
|
||||
is_negative := msg < 0
|
||||
|
||||
if is_negative {
|
||||
for i := 0; msg != 0; i++ {
|
||||
coeffs[i] = uint64(modulus + (msg % base))
|
||||
msg /= base
|
||||
}
|
||||
} else {
|
||||
for i := 0; msg != 0; i++ {
|
||||
coeffs[i] = uint64(msg % base)
|
||||
msg /= base
|
||||
}
|
||||
}
|
||||
|
||||
return coeffs
|
||||
}
|
||||
|
||||
// Decodes a integer from a ring F given a base W
|
||||
// This is equivalent to evaluating F with the base W
|
||||
// This evaluation is done with Horner's method (O(N))
|
||||
func intDecode(coeffs []uint64, base, modulus int64) (msg int64) {
|
||||
|
||||
msg = 0
|
||||
modulus_half := uint64(modulus >> 1)
|
||||
for i := len(coeffs) - 1; i >= 0; i-- {
|
||||
msg *= base
|
||||
msg += int64(coeffs[i])
|
||||
if coeffs[i] > modulus_half {
|
||||
msg -= modulus
|
||||
}
|
||||
}
|
||||
|
||||
return msg
|
||||
}
|
||||
75
bfv/encryptor.go
Normal file
75
bfv/encryptor.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package bfv
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
)
|
||||
|
||||
type Encryptor struct {
|
||||
bfvcontext *BfvContext
|
||||
pk *PublicKey // secret key
|
||||
polypool *ring.Poly
|
||||
}
|
||||
|
||||
func (bfvcontext *BfvContext) NewEncryptor(pk *PublicKey) (*Encryptor, error) {
|
||||
if uint64(pk.pk[0].GetDegree()+pk.pk[1].GetDegree())>>1 != bfvcontext.n {
|
||||
return nil, errors.New("error : pk ring degree doesn't match bfvcontext ring degree")
|
||||
}
|
||||
//TODO : check that pk degree matches bfvContext degree
|
||||
encryptor := new(Encryptor)
|
||||
encryptor.bfvcontext = bfvcontext
|
||||
encryptor.pk = pk
|
||||
encryptor.polypool = bfvcontext.contextQ.NewPoly()
|
||||
return encryptor, nil
|
||||
}
|
||||
|
||||
func (encryptor *Encryptor) EncryptNew(plaintext *Plaintext) (*Ciphertext, error) {
|
||||
|
||||
if uint64(plaintext.value[0].GetDegree()) != encryptor.bfvcontext.n {
|
||||
return nil, errors.New("error : plaintext ring degree doesn't match encryptor bfvcontext ring degree")
|
||||
}
|
||||
|
||||
ciphertext := encryptor.bfvcontext.NewCiphertext(1)
|
||||
|
||||
encrypt(encryptor, plaintext, ciphertext)
|
||||
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
func (encryptor *Encryptor) Encrypt(plaintext *Plaintext, ciphertext *Ciphertext) error {
|
||||
|
||||
if uint64(plaintext.value[0].GetDegree()) != encryptor.bfvcontext.n {
|
||||
return errors.New("error : plaintext ring degree doesn't match encryptor bfvcontext ring degree")
|
||||
}
|
||||
|
||||
if ciphertext.Degree() != 1 {
|
||||
return errors.New("error : invalide receiver -> ciphertext degree > 1")
|
||||
}
|
||||
|
||||
encrypt(encryptor, plaintext, ciphertext)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func encrypt(encryptor *Encryptor, plaintext *Plaintext, ciphertext *Ciphertext) {
|
||||
|
||||
context := encryptor.bfvcontext.contextQ
|
||||
|
||||
encryptor.bfvcontext.ternarySampler.SampleMontgomeryNTT(encryptor.polypool)
|
||||
|
||||
context.MulCoeffsMontgomery(encryptor.polypool, encryptor.pk.pk[0], ciphertext.value[0])
|
||||
context.MulCoeffsMontgomery(encryptor.polypool, encryptor.pk.pk[1], ciphertext.value[1])
|
||||
|
||||
context.InvNTT(ciphertext.value[0], ciphertext.value[0])
|
||||
context.InvNTT(ciphertext.value[1], ciphertext.value[1])
|
||||
|
||||
context.Add(ciphertext.value[0], plaintext.value[0], ciphertext.value[0])
|
||||
|
||||
encryptor.bfvcontext.gaussianSampler.Sample(encryptor.polypool)
|
||||
context.Add(ciphertext.value[0], encryptor.polypool, ciphertext.value[0])
|
||||
|
||||
encryptor.bfvcontext.gaussianSampler.Sample(encryptor.polypool)
|
||||
context.Add(ciphertext.value[1], encryptor.polypool, ciphertext.value[1])
|
||||
|
||||
encryptor.polypool.Zero()
|
||||
}
|
||||
812
bfv/evaluator.go
Normal file
812
bfv/evaluator.go
Normal file
@@ -0,0 +1,812 @@
|
||||
package bfv
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
)
|
||||
|
||||
type Evaluator struct {
|
||||
bfvcontext *BfvContext
|
||||
basisextender *ring.BasisExtender
|
||||
complexscaler *ring.ComplexScaler
|
||||
polypool [10]*ring.Poly
|
||||
ctxpool [3]*Ciphertext
|
||||
}
|
||||
|
||||
func (bfvcontext *BfvContext) NewEvaluator() (*Evaluator, error) {
|
||||
var err error
|
||||
|
||||
evaluator := new(Evaluator)
|
||||
evaluator.bfvcontext = bfvcontext
|
||||
|
||||
if evaluator.basisextender, err = ring.NewBasisExtender(bfvcontext.contextQ, bfvcontext.contextP); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if evaluator.complexscaler, err = ring.NewComplexScaler(bfvcontext.t, bfvcontext.contextQ, bfvcontext.contextP); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
evaluator.polypool[i] = bfvcontext.contextQ.NewPoly()
|
||||
}
|
||||
|
||||
evaluator.ctxpool[0] = bfvcontext.NewCiphertextBig(10)
|
||||
evaluator.ctxpool[1] = bfvcontext.NewCiphertextBig(10)
|
||||
evaluator.ctxpool[2] = bfvcontext.NewCiphertextBig(10)
|
||||
|
||||
return evaluator, nil
|
||||
}
|
||||
|
||||
// AddNoMod adds the second element to the first element.
|
||||
// Requires a receiver of correct type and degree.
|
||||
func (evaluator *Evaluator) Add(c0, c1, cOut BfvElement) (err error) {
|
||||
|
||||
if err = evaluateInPlace(c0, c1, cOut, evaluator.bfvcontext.contextQ.Add); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddNoMod adds the second element to the first element without modular reduction.
|
||||
// Requires a receiver of correct type and degree.
|
||||
func (evaluator *Evaluator) AddNoMod(c0, c1, cOut BfvElement) (err error) {
|
||||
|
||||
if err = evaluateInPlace(c0, c1, cOut, evaluator.bfvcontext.contextQ.AddNoMod); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddNew adds the second elements to the first element and creates a new element storing the result.
|
||||
// Be aware that an element of type ciphertext is always returned, even if two plaintexts where given as input.
|
||||
func (evaluator *Evaluator) AddNew(c0, c1 BfvElement) (cOut BfvElement, err error) {
|
||||
|
||||
if cOut, err = evaluateNew(c0, c1, evaluator.bfvcontext.contextQ.Add, evaluator.bfvcontext); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// AddNoModNew adds the second elements to the first element without modular reduction and creates a new element storing the result.
|
||||
// Be aware that an element of type ciphertext is always returned, even if two plaintexts where given as input.
|
||||
func (evaluator *Evaluator) AddNoModNew(c0, c1 BfvElement) (cOut BfvElement, err error) {
|
||||
|
||||
if cOut, err = evaluateNew(c0, c1, evaluator.bfvcontext.contextQ.AddNoMod, evaluator.bfvcontext); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Sub subtract the second element to the first element.
|
||||
// Requires a receiver of correct type and degree.
|
||||
func (evaluator *Evaluator) Sub(c0, c1, cOut BfvElement) (err error) {
|
||||
|
||||
if err = evaluateInPlace(c0, c1, cOut, evaluator.bfvcontext.contextQ.Sub); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sub subtract the second element to the first element without modular reduction.
|
||||
// Requires a receiver of correct type and degree.
|
||||
func (evaluator *Evaluator) SubNoMod(c0, c1, cOut BfvElement) (err error) {
|
||||
|
||||
if err = evaluateInPlace(c0, c1, cOut, evaluator.bfvcontext.contextQ.SubNoMod); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SubNew subtracts the second elements to the first element and creates a new element storing the result.
|
||||
// Be aware that an element of type ciphertext is always returned, even if two plaintexts where given as input.
|
||||
func (evaluator *Evaluator) SubNew(c0, c1 BfvElement) (cOut BfvElement, err error) {
|
||||
|
||||
if cOut, err = evaluateNew(c0, c1, evaluator.bfvcontext.contextQ.Sub, evaluator.bfvcontext); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SubNoModNew subtracts the second elements to the first element without modular reduction and creates a new element storing the result.
|
||||
// Be aware that an element of type ciphertext is always returned, even if two plaintexts where given as input.
|
||||
func (evaluator *Evaluator) SubNoModNew(c0, c1 BfvElement) (cOut BfvElement, err error) {
|
||||
|
||||
if cOut, err = evaluateNew(c0, c1, evaluator.bfvcontext.contextQ.SubNoMod, evaluator.bfvcontext); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// evaluateInPlace applies the provided function in place on c0 and c1 and returns the result in cOut
|
||||
func evaluateInPlace(c0, c1, cOut BfvElement, evaluate func(*ring.Poly, *ring.Poly, *ring.Poly)) (err error) {
|
||||
|
||||
if !checkContext([]BfvElement{c0, c1, cOut}) {
|
||||
return errors.New("cannot evaluate -> input elements do not share the same ckkscontext")
|
||||
}
|
||||
|
||||
maxDegree := max([]uint64{c0.Degree(), c1.Degree()})
|
||||
minDegree := min([]uint64{c0.Degree(), c1.Degree()})
|
||||
|
||||
// Checks the validity of the receiver element
|
||||
if cOut.Degree() == 0 && cOut.Degree() < maxDegree {
|
||||
return errors.New("cannot evaluate(c0, c1 cOut) -> cOut is a plaintext (or an invalid ciphertext of degree 0) while c1 and/or c2 are ciphertexts of degree >= 1")
|
||||
} else {
|
||||
// Else resizes the receiver element
|
||||
cOut.Resize(maxDegree)
|
||||
}
|
||||
|
||||
for i := uint64(0); i < minDegree+1; i++ {
|
||||
evaluate(c0.Value()[i], c1.Value()[i], cOut.Value()[i])
|
||||
}
|
||||
|
||||
// If the inputs degree differ, copies the remaining degree on the receiver
|
||||
// Also checks that the receiver is ont one of the inputs to avoid unnecessary work.
|
||||
|
||||
if c0.Degree() > c1.Degree() && c0 != cOut {
|
||||
for i := minDegree + 1; i < maxDegree+1; i++ {
|
||||
c0.Value()[i].Copy(cOut.Value()[i])
|
||||
}
|
||||
} else if c1.Degree() > c0.Degree() && c1 != cOut {
|
||||
for i := minDegree + 1; i < maxDegree+1; i++ {
|
||||
c1.Value()[i].Copy(cOut.Value()[i])
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// evaluateNew applies the provided function on c0 and c1 and returns the result on a new element cOut.
|
||||
func evaluateNew(c0, c1 BfvElement, evaluate func(*ring.Poly, *ring.Poly, *ring.Poly), bfvcontext *BfvContext) (cOut BfvElement, err error) {
|
||||
|
||||
if !checkContext([]BfvElement{c0, c1, cOut}) {
|
||||
return nil, errors.New("cannot evaluate -> input elements do not share the same ckkscontext")
|
||||
}
|
||||
|
||||
if c0.Degree() >= c1.Degree() {
|
||||
|
||||
cOut = c0.CopyNew()
|
||||
|
||||
for i := range c1.Value() {
|
||||
evaluate(cOut.Value()[i], c1.Value()[i], cOut.Value()[i])
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
cOut = c1.CopyNew()
|
||||
|
||||
for i := range c0.Value() {
|
||||
evaluate(cOut.Value()[i], c0.Value()[i], cOut.Value()[i])
|
||||
}
|
||||
}
|
||||
|
||||
return cOut, nil
|
||||
}
|
||||
|
||||
// Neg negates the input element.
|
||||
// Requiers a receiver of the correct type and degree.
|
||||
func (evaluator *Evaluator) Neg(c0, cOut BfvElement) error {
|
||||
|
||||
if !checkContext([]BfvElement{c0, cOut}) {
|
||||
return errors.New("input elements are not using the same ckkscontext")
|
||||
}
|
||||
|
||||
if c0.Degree() != cOut.Degree() {
|
||||
return errors.New("error : invalid receiver ciphertext (degree not equal to input ciphertext")
|
||||
}
|
||||
|
||||
for i := range c0.Value() {
|
||||
evaluator.bfvcontext.contextQ.Neg(c0.Value()[i], cOut.Value()[i])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NegNew negates the input element and returns a new element storing the result.
|
||||
// Be aware that an element of type ciphertext is always returned, even if two plaintexts where given as input.
|
||||
func (evaluator *Evaluator) NegNew(c0 BfvElement) (cOut BfvElement) {
|
||||
|
||||
if c0.Degree() == 0 {
|
||||
cOut = evaluator.bfvcontext.NewPlaintext()
|
||||
} else {
|
||||
cOut = evaluator.bfvcontext.NewCiphertext(c0.Degree())
|
||||
}
|
||||
|
||||
for i := range c0.Value() {
|
||||
evaluator.bfvcontext.contextQ.Neg(c0.Value()[i], cOut.Value()[i])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reduce applies a modular reduction on the input element.
|
||||
// Requires a receiver element of the correct type and degree.
|
||||
func (evaluator *Evaluator) Reduce(c0, cOut BfvElement) error {
|
||||
|
||||
if !checkContext([]BfvElement{c0, cOut}) {
|
||||
return errors.New("input elements are not using the same ckkscontext")
|
||||
}
|
||||
|
||||
if c0.Degree() != cOut.Degree() {
|
||||
return errors.New("error : invalide ciphertext receiver (degree doesn't match c0.Degree")
|
||||
}
|
||||
|
||||
for i := range c0.Value() {
|
||||
evaluator.bfvcontext.contextQ.Reduce(c0.Value()[i], cOut.Value()[i])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reduce applies a modular reduction on the input element and returns a new element storing the result.
|
||||
// Be aware that an element of type ciphertext is always returned, even if two plaintexts where given as input.
|
||||
func (evaluator *Evaluator) ReduceNew(c0 BfvElement) (cOut BfvElement) {
|
||||
|
||||
if c0.Degree() == 0 {
|
||||
cOut = evaluator.bfvcontext.NewPlaintext()
|
||||
} else {
|
||||
cOut = evaluator.bfvcontext.NewCiphertext(c0.Degree())
|
||||
}
|
||||
|
||||
evaluator.Reduce(c0, cOut)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// MulScalar multiplies the input by the given scalar.
|
||||
// Requirese a receiver of the correct type and degree.
|
||||
func (evaluator *Evaluator) MulScalar(c0 BfvElement, scalar uint64, cOut BfvElement) error {
|
||||
|
||||
if !checkContext([]BfvElement{c0, cOut}) {
|
||||
return errors.New("input elements are not using the same ckkscontext")
|
||||
}
|
||||
|
||||
if c0.Degree() != cOut.Degree() {
|
||||
return errors.New("error : invalide ciphertext receiver (degree doesn't match c0.Degree")
|
||||
}
|
||||
|
||||
for i := range c0.Value() {
|
||||
evaluator.bfvcontext.contextQ.MulScalar(c0.Value()[i], scalar, cOut.Value()[i])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MulScalar multiplies the input by the given scalar and returns an new elemnent storing the result.
|
||||
// Be aware that an element of type ciphertext is always returned, even if two plaintexts where given as input.
|
||||
func (evaluator *Evaluator) MulScalarNew(c0 BfvElement, scalar uint64) (cOut BfvElement) {
|
||||
|
||||
if c0.Degree() == 0 {
|
||||
cOut = evaluator.bfvcontext.NewPlaintext()
|
||||
} else {
|
||||
cOut = evaluator.bfvcontext.NewCiphertext(c0.Degree())
|
||||
}
|
||||
|
||||
evaluator.MulScalar(c0, scalar, cOut)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Mul multiplies the first element by the second element.
|
||||
// Requirese a receiver of the correct type and degree.
|
||||
func (evaluator *Evaluator) Mul(c0, c1, cOut BfvElement) (err error) {
|
||||
|
||||
if !checkContext([]BfvElement{c0, cOut}) {
|
||||
return errors.New("input elements are not using the same ckkscontext")
|
||||
}
|
||||
|
||||
tensorAndRescale(evaluator, c0.Value(), c1.Value(), cOut.Value())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MulNew multiplies the first element by the second element and returns the result in a new element.
|
||||
// Be aware that an element of type ciphertext is always returned, even if two plaintexts where given as input.
|
||||
func (evaluator *Evaluator) MulNew(c0, c1 BfvElement) (cOut BfvElement) {
|
||||
|
||||
if c0.Degree()+c1.Degree() == 0 {
|
||||
cOut = evaluator.bfvcontext.NewPlaintext()
|
||||
} else {
|
||||
cOut = evaluator.bfvcontext.NewCiphertext(c0.Degree() + c1.Degree())
|
||||
}
|
||||
|
||||
tensorAndRescale(evaluator, c0.Value(), c1.Value(), cOut.Value())
|
||||
|
||||
return cOut
|
||||
}
|
||||
|
||||
// tensorAndRescales applies a tensoring operation between the first elent and the second element, followed by a
|
||||
// rescaling.
|
||||
func tensorAndRescale(evaluator *Evaluator, ct0, ct1, cOut []*ring.Poly) {
|
||||
|
||||
// Prepares the ciphertexts for the Tensoring by extending their
|
||||
// basis from Q to QP and transforming them in NTT form
|
||||
c0 := evaluator.ctxpool[0]
|
||||
c1 := evaluator.ctxpool[1]
|
||||
tmpCout := evaluator.ctxpool[2]
|
||||
|
||||
for i := range ct0 {
|
||||
evaluator.basisextender.ExtendBasis(ct0[i], c0.Value()[i])
|
||||
evaluator.bfvcontext.contextQP.NTT(c0.Value()[i], c0.Value()[i])
|
||||
}
|
||||
|
||||
for i := range ct1 {
|
||||
evaluator.basisextender.ExtendBasis(ct1[i], c1.Value()[i])
|
||||
evaluator.bfvcontext.contextQP.NTT(c1.Value()[i], c1.Value()[i])
|
||||
}
|
||||
|
||||
// Tensoring : multiplies each elements of the ciphertexts together
|
||||
// and adds them to their correspongint position in the new ciphertext
|
||||
// based on their respective degree
|
||||
|
||||
for i := 0; i < len(ct0)+len(ct1); i++ {
|
||||
tmpCout.Value()[i].Zero()
|
||||
}
|
||||
|
||||
for i := range ct0 {
|
||||
evaluator.bfvcontext.contextQP.MForm(c0.Value()[i], c0.Value()[i])
|
||||
for j := range ct1 {
|
||||
evaluator.bfvcontext.contextQP.MulCoeffsMontgomeryAndAdd(c0.Value()[i], c1.Value()[j], tmpCout.Value()[i+j])
|
||||
}
|
||||
}
|
||||
|
||||
// Applies the inverse NTT to the ciphertext, scales the down ciphertext
|
||||
// by t/q and reduces its basis from QP to Q
|
||||
for i := range cOut {
|
||||
evaluator.bfvcontext.contextQP.InvNTT(tmpCout.Value()[i], tmpCout.Value()[i])
|
||||
evaluator.complexscaler.Scale(tmpCout.Value()[i], cOut[i])
|
||||
}
|
||||
}
|
||||
|
||||
func (evaluator *Evaluator) Relinearize(cIn *Ciphertext, evakey *EvaluationKey, cOut *Ciphertext) error {
|
||||
|
||||
if int(cIn.Degree()-1) > len(evakey.evakey) {
|
||||
return errors.New("error : ciphertext degree too large to allow relinearization")
|
||||
}
|
||||
|
||||
if cIn.Degree() < 2 {
|
||||
return errors.New("error : ciphertext is already of degree 1 or 0")
|
||||
}
|
||||
|
||||
relinearize(evaluator, cIn, evakey, cOut)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (evaluator *Evaluator) RelinearizeNew(cIn *Ciphertext, evakey *EvaluationKey) (*Ciphertext, error) {
|
||||
|
||||
if int(cIn.Degree()-1) > len(evakey.evakey) {
|
||||
return nil, errors.New("error : ciphertext degree too large to allow relinearization")
|
||||
}
|
||||
|
||||
if cIn.Degree() < 2 {
|
||||
return nil, errors.New("error : ciphertext is already of degree 1 or 0")
|
||||
}
|
||||
|
||||
cOut := evaluator.bfvcontext.NewCiphertext(1)
|
||||
|
||||
relinearize(evaluator, cIn, evakey, cOut)
|
||||
|
||||
return cOut, nil
|
||||
}
|
||||
|
||||
func relinearize(evaluator *Evaluator, cIn *Ciphertext, evakey *EvaluationKey, cOut *Ciphertext) {
|
||||
|
||||
evaluator.bfvcontext.contextQ.NTT(cIn.Value()[0], cOut.Value()[0])
|
||||
evaluator.bfvcontext.contextQ.NTT(cIn.Value()[1], cOut.Value()[1])
|
||||
|
||||
for deg := uint64(cIn.Degree()); deg > 1; deg-- {
|
||||
switchKeys(evaluator, cOut.Value()[0], cOut.Value()[1], cIn.Value()[deg], evakey.evakey[deg-2], cOut)
|
||||
}
|
||||
|
||||
if len(cOut.Value()) > 2 {
|
||||
cOut.SetValue(cOut.Value()[:2])
|
||||
}
|
||||
|
||||
evaluator.bfvcontext.contextQ.InvNTT(cOut.Value()[0], cOut.Value()[0])
|
||||
evaluator.bfvcontext.contextQ.InvNTT(cOut.Value()[1], cOut.Value()[1])
|
||||
}
|
||||
|
||||
func (evaluator *Evaluator) SwitchKeys(cIn *Ciphertext, switchingKey *SwitchingKey, cOut *Ciphertext) error {
|
||||
|
||||
if cIn.Degree() != 1 {
|
||||
return errors.New("error : ciphertext must be of degree 1 to allow key switching")
|
||||
}
|
||||
|
||||
if cOut.Degree() != 1 {
|
||||
return errors.New("error : receiver ciphertext must be of degree 1 to allow key switching")
|
||||
}
|
||||
|
||||
var c2 *ring.Poly
|
||||
if cIn == cOut {
|
||||
c2 = evaluator.polypool[1]
|
||||
cIn.Value()[1].Copy(c2)
|
||||
} else {
|
||||
c2 = cIn.Value()[1]
|
||||
}
|
||||
cIn.NTT(cOut)
|
||||
switchKeys(evaluator, cOut.Value()[0], cOut.Value()[1], c2, switchingKey, cOut)
|
||||
cOut.InvNTT(cOut)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (evaluator *Evaluator) SwitchKeysNew(cIn *Ciphertext, switchingKey *SwitchingKey) (*Ciphertext, error) {
|
||||
|
||||
if cIn.Degree() > 1 {
|
||||
return nil, errors.New("error : ciphertext must be of degree 1 to allow key switching")
|
||||
}
|
||||
|
||||
cOut := evaluator.bfvcontext.NewCiphertext(1)
|
||||
|
||||
cIn.NTT(cOut)
|
||||
switchKeys(evaluator, cOut.Value()[0], cOut.Value()[1], cIn.Value()[1], switchingKey, cOut)
|
||||
cOut.InvNTT(cOut)
|
||||
|
||||
return cOut, nil
|
||||
}
|
||||
|
||||
// RotateColumns homomorphically rotates the columns of the ciphertext to the left
|
||||
func (evaluator *Evaluator) RotateColumns(c0 BfvElement, k uint64, evakey *RotationKeys, c1 BfvElement) (err error) {
|
||||
|
||||
k &= ((evaluator.bfvcontext.n >> 1) - 1)
|
||||
|
||||
if k == 0 {
|
||||
if c0 != c1 {
|
||||
if err = c0.Copy(c1); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if c1.Degree() != c0.Degree() {
|
||||
return errors.New("cannot rotate -> receiver degree doesn't match input degree ")
|
||||
}
|
||||
|
||||
if c0.Degree() > 1 {
|
||||
return errors.New("cannot rotate -> input and or output degree not 0 or 1")
|
||||
}
|
||||
|
||||
context := evaluator.bfvcontext.contextQ
|
||||
|
||||
if c0.Degree() == 0 {
|
||||
|
||||
if c1 != c0 {
|
||||
|
||||
if c0.IsNTT() {
|
||||
ring.PermuteNTT(c0.Value()[0], evaluator.bfvcontext.galElRotColLeft[k], c1.Value()[0])
|
||||
} else {
|
||||
context.Permute(c0.Value()[0], evaluator.bfvcontext.galElRotColLeft[k], c1.Value()[0])
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if c0.IsNTT() {
|
||||
ring.PermuteNTT(c0.Value()[0], evaluator.bfvcontext.galElRotColLeft[k], evaluator.polypool[0])
|
||||
} else {
|
||||
context.Permute(c0.Value()[0], evaluator.bfvcontext.galElRotColLeft[k], evaluator.polypool[0])
|
||||
}
|
||||
|
||||
evaluator.polypool[0].Copy(c1.Value()[0])
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
} else {
|
||||
// Looks in the rotationkey if the corresponding rotation has been generated
|
||||
if evakey.evakey_rot_col_L[k] != nil {
|
||||
|
||||
if c0.IsNTT() {
|
||||
|
||||
ring.PermuteNTT(c0.Value()[0], evaluator.bfvcontext.galElRotColLeft[k], evaluator.polypool[0])
|
||||
ring.PermuteNTT(c0.Value()[1], evaluator.bfvcontext.galElRotColLeft[k], evaluator.polypool[1])
|
||||
|
||||
evaluator.polypool[0].Copy(c1.Value()[0])
|
||||
evaluator.polypool[1].Copy(c1.Value()[1])
|
||||
|
||||
context.InvNTT(evaluator.polypool[1], evaluator.polypool[1])
|
||||
|
||||
switchKeys(evaluator, c1.Value()[0], c1.Value()[1], evaluator.polypool[1], evakey.evakey_rot_col_L[k], c1.(*Ciphertext))
|
||||
|
||||
} else {
|
||||
|
||||
context.Permute(c0.Value()[0], evaluator.bfvcontext.galElRotColLeft[k], evaluator.polypool[0])
|
||||
context.Permute(c0.Value()[1], evaluator.bfvcontext.galElRotColLeft[k], evaluator.polypool[1])
|
||||
|
||||
context.NTT(evaluator.polypool[0], c1.Value()[0])
|
||||
context.NTT(evaluator.polypool[1], c1.Value()[1])
|
||||
|
||||
switchKeys(evaluator, c1.Value()[0], c1.Value()[1], evaluator.polypool[1], evakey.evakey_rot_col_L[k], c1.(*Ciphertext))
|
||||
|
||||
context.InvNTT(c1.Value()[0], c1.Value()[0])
|
||||
context.InvNTT(c1.Value()[1], c1.Value()[1])
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
} else {
|
||||
|
||||
// If not looks if the left and right pow2 rotations have been generated
|
||||
has_pow2_rotations := true
|
||||
for i := uint64(1); i < evaluator.bfvcontext.n>>1; i <<= 1 {
|
||||
if evakey.evakey_rot_col_L[i] == nil || evakey.evakey_rot_col_R[i] == nil {
|
||||
has_pow2_rotations = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If yes, computes the least amount of rotation between left and right required to apply the demanded rotation
|
||||
if has_pow2_rotations {
|
||||
|
||||
if hammingWeight64(k) <= hammingWeight64((evaluator.bfvcontext.n>>1)-k) {
|
||||
rotateColumnsLPow2(evaluator, c0, k, evakey, c1)
|
||||
} else {
|
||||
rotateColumnsRPow2(evaluator, c0, (evaluator.bfvcontext.n>>1)-k, evakey, c1)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
// Else returns an error indicating that the keys have not been generated
|
||||
} else {
|
||||
return errors.New("error : specific rotation and pow2 rotations have not been generated")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Applies the Galois Automorphism on the Ciphertext, using the Plaintext Primitive Roots
|
||||
func rotateColumnsLPow2(evaluator *Evaluator, c0 BfvElement, k uint64, evakey *RotationKeys, c1 BfvElement) {
|
||||
rotateColumnsPow2(evaluator, c0, evaluator.bfvcontext.gen, k, evakey.evakey_rot_col_L, c1)
|
||||
}
|
||||
|
||||
// Applies the Galois Endomorphism on the Ciphertext, using the Plaintext Primitive Roots
|
||||
func rotateColumnsRPow2(evaluator *Evaluator, c0 BfvElement, k uint64, evakey *RotationKeys, c1 BfvElement) {
|
||||
rotateColumnsPow2(evaluator, c0, evaluator.bfvcontext.genInv, k, evakey.evakey_rot_col_R, c1)
|
||||
}
|
||||
|
||||
func rotateColumnsPow2(evaluator *Evaluator, c0 BfvElement, generator, k uint64, evakey_rot_col map[uint64]*SwitchingKey, c1 BfvElement) {
|
||||
|
||||
var mask, evakey_index uint64
|
||||
|
||||
context := evaluator.bfvcontext.contextQ
|
||||
|
||||
mask = (evaluator.bfvcontext.n << 1) - 1
|
||||
|
||||
evakey_index = 1
|
||||
|
||||
if c0.IsNTT() {
|
||||
c0.Copy(c1)
|
||||
} else {
|
||||
for i := range c0.Value() {
|
||||
context.NTT(c0.Value()[i], c1.Value()[i])
|
||||
}
|
||||
}
|
||||
|
||||
// Applies the galois automorphism and the switching-key process
|
||||
for k > 0 {
|
||||
|
||||
if k&1 == 1 {
|
||||
|
||||
if c0.Degree() == 0 {
|
||||
|
||||
ring.PermuteNTT(c1.Value()[0], generator, evaluator.polypool[0])
|
||||
evaluator.polypool[0].Copy(c1.Value()[0])
|
||||
|
||||
} else {
|
||||
|
||||
ring.PermuteNTT(c1.Value()[0], generator, evaluator.polypool[0])
|
||||
ring.PermuteNTT(c1.Value()[1], generator, evaluator.polypool[1])
|
||||
|
||||
evaluator.polypool[0].Copy(c1.Value()[0])
|
||||
evaluator.polypool[1].Copy(c1.Value()[1])
|
||||
context.InvNTT(evaluator.polypool[1], evaluator.polypool[2])
|
||||
|
||||
switchKeys(evaluator, evaluator.polypool[0], evaluator.polypool[1], evaluator.polypool[2], evakey_rot_col[evakey_index], c1.(*Ciphertext))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
generator *= generator
|
||||
generator &= mask
|
||||
|
||||
evakey_index <<= 1
|
||||
k >>= 1
|
||||
}
|
||||
|
||||
if !c0.IsNTT() {
|
||||
for i := range c1.Value() {
|
||||
context.InvNTT(c1.Value()[i], c1.Value()[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Applies the Galois Endomorphism on the Ciphertext, using the Plaintext Primitive Roots
|
||||
func (evaluator *Evaluator) RotateRows(c0 BfvElement, evakey *RotationKeys, c1 BfvElement) error {
|
||||
|
||||
if c1.Degree() != c0.Degree() {
|
||||
return errors.New("cannot rotate -> receiver degree doesn't match input degree ")
|
||||
}
|
||||
|
||||
if c0.Degree() > 1 {
|
||||
return errors.New("cannot rotate -> input and or output degree not 0 or 1")
|
||||
}
|
||||
|
||||
if evakey.evakey_rot_row == nil {
|
||||
return errors.New("error : rows rotation key not generated")
|
||||
}
|
||||
|
||||
context := evaluator.bfvcontext.contextQ
|
||||
|
||||
if c0.Degree() == 0 {
|
||||
|
||||
if c0.IsNTT() {
|
||||
|
||||
if c0 != c1 {
|
||||
|
||||
ring.PermuteNTT(c0.Value()[0], evaluator.bfvcontext.galElRotRow, c1.Value()[0])
|
||||
|
||||
} else {
|
||||
|
||||
ring.PermuteNTT(c0.Value()[0], evaluator.bfvcontext.galElRotRow, evaluator.polypool[0])
|
||||
evaluator.polypool[0].Copy(c1.Value()[0])
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if c0 != c1 {
|
||||
|
||||
context.Permute(c0.Value()[0], evaluator.bfvcontext.galElRotRow, c1.Value()[0])
|
||||
|
||||
} else {
|
||||
|
||||
context.Permute(c0.Value()[0], evaluator.bfvcontext.galElRotRow, evaluator.polypool[0])
|
||||
evaluator.polypool[0].Copy(c1.Value()[0])
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if c0.IsNTT() {
|
||||
|
||||
if c0 != c1 {
|
||||
|
||||
ring.PermuteNTT(c0.Value()[0], evaluator.bfvcontext.galElRotRow, c1.Value()[0])
|
||||
ring.PermuteNTT(c0.Value()[1], evaluator.bfvcontext.galElRotRow, c1.Value()[1])
|
||||
|
||||
context.InvNTT(c1.Value()[1], evaluator.polypool[1])
|
||||
|
||||
switchKeys(evaluator, c1.Value()[0], c1.Value()[1], evaluator.polypool[1], evakey.evakey_rot_row, c1.(*Ciphertext))
|
||||
|
||||
} else {
|
||||
|
||||
ring.PermuteNTT(c0.Value()[0], evaluator.bfvcontext.galElRotRow, evaluator.polypool[0])
|
||||
ring.PermuteNTT(c0.Value()[1], evaluator.bfvcontext.galElRotRow, evaluator.polypool[1])
|
||||
|
||||
evaluator.polypool[0].Copy(c1.Value()[0])
|
||||
evaluator.polypool[1].Copy(c1.Value()[1])
|
||||
|
||||
context.InvNTT(evaluator.polypool[1], evaluator.polypool[1])
|
||||
|
||||
switchKeys(evaluator, c1.Value()[0], c1.Value()[1], evaluator.polypool[1], evakey.evakey_rot_row, c1.(*Ciphertext))
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if c0 != c1 {
|
||||
|
||||
context.Permute(c0.Value()[0], evaluator.bfvcontext.galElRotRow, c1.Value()[0])
|
||||
context.Permute(c0.Value()[1], evaluator.bfvcontext.galElRotRow, c1.Value()[1])
|
||||
|
||||
c1.Value()[1].Copy(evaluator.polypool[1])
|
||||
|
||||
context.NTT(c1.Value()[0], c1.Value()[0])
|
||||
context.NTT(c1.Value()[1], c1.Value()[1])
|
||||
|
||||
switchKeys(evaluator, c1.Value()[0], c1.Value()[1], evaluator.polypool[1], evakey.evakey_rot_row, c1.(*Ciphertext))
|
||||
|
||||
context.InvNTT(c1.Value()[0], c1.Value()[0])
|
||||
context.InvNTT(c1.Value()[1], c1.Value()[1])
|
||||
|
||||
} else {
|
||||
|
||||
context.Permute(c0.Value()[0], evaluator.bfvcontext.galElRotRow, evaluator.polypool[0])
|
||||
context.Permute(c0.Value()[1], evaluator.bfvcontext.galElRotRow, evaluator.polypool[1])
|
||||
|
||||
context.NTT(evaluator.polypool[0], c1.Value()[0])
|
||||
context.NTT(evaluator.polypool[1], c1.Value()[1])
|
||||
|
||||
switchKeys(evaluator, c1.Value()[0], c1.Value()[1], evaluator.polypool[1], evakey.evakey_rot_row, c1.(*Ciphertext))
|
||||
|
||||
context.InvNTT(c1.Value()[0], c1.Value()[0])
|
||||
context.InvNTT(c1.Value()[1], c1.Value()[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (evaluator *Evaluator) InnerSum(c0 *Ciphertext, evakey *RotationKeys, c1 *Ciphertext) error {
|
||||
if c0.Degree() != 1 {
|
||||
return errors.New("error : ciphertext must be of degree 1 to allow Galois Auotomorphism (required for inner sum)")
|
||||
}
|
||||
|
||||
if c1.Degree() != 1 {
|
||||
return errors.New("error : receiver ciphertext must be of degree 1 to allow Galois Automorphism (required for inner sum)")
|
||||
}
|
||||
|
||||
cTmp := evaluator.bfvcontext.NewCiphertext(1)
|
||||
|
||||
if c0 != c1 {
|
||||
if err := c1.Copy(c0); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for i := uint64(1); i < evaluator.bfvcontext.n>>1; i <<= 1 {
|
||||
if err := evaluator.RotateColumns(c1, i, evakey, cTmp); err != nil {
|
||||
return err
|
||||
}
|
||||
evaluator.Add(cTmp, c1, c1)
|
||||
}
|
||||
|
||||
if err := evaluator.RotateRows(c1, evakey, cTmp); err != nil {
|
||||
return err
|
||||
}
|
||||
evaluator.Add(c1, cTmp, c1)
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func switchKeys(evaluator *Evaluator, c0, c1, c2 *ring.Poly, evakey *SwitchingKey, cOut *Ciphertext) {
|
||||
|
||||
var mask, reduce, bitLog uint64
|
||||
|
||||
c2_qi_w := evaluator.polypool[4]
|
||||
|
||||
mask = uint64(((1 << evakey.bitDecomp) - 1))
|
||||
|
||||
reduce = 0
|
||||
|
||||
for i := range evaluator.bfvcontext.contextQ.Modulus {
|
||||
|
||||
bitLog = uint64(len(evakey.evakey[i]))
|
||||
|
||||
for j := uint64(0); j < bitLog; j++ {
|
||||
//c2_qi_w = (c2_qi_w >> (w*z)) & (w-1)
|
||||
for u := uint64(0); u < evaluator.bfvcontext.n; u++ {
|
||||
for v := range evaluator.bfvcontext.contextQ.Modulus {
|
||||
c2_qi_w.Coeffs[v][u] = (c2.Coeffs[i][u] >> (j * evakey.bitDecomp)) & mask
|
||||
}
|
||||
}
|
||||
|
||||
evaluator.bfvcontext.contextQ.NTT(c2_qi_w, c2_qi_w)
|
||||
|
||||
evaluator.bfvcontext.contextQ.MulCoeffsMontgomeryAndAddNoMod(evakey.evakey[i][j][0], c2_qi_w, cOut.Value()[0])
|
||||
evaluator.bfvcontext.contextQ.MulCoeffsMontgomeryAndAddNoMod(evakey.evakey[i][j][1], c2_qi_w, cOut.Value()[1])
|
||||
|
||||
if reduce&7 == 7 {
|
||||
evaluator.bfvcontext.contextQ.Reduce(cOut.Value()[0], cOut.Value()[0])
|
||||
evaluator.bfvcontext.contextQ.Reduce(cOut.Value()[1], cOut.Value()[1])
|
||||
}
|
||||
|
||||
reduce += 1
|
||||
}
|
||||
}
|
||||
|
||||
if (reduce-1)&7 != 7 {
|
||||
evaluator.bfvcontext.contextQ.Reduce(cOut.Value()[0], cOut.Value()[0])
|
||||
evaluator.bfvcontext.contextQ.Reduce(cOut.Value()[1], cOut.Value()[1])
|
||||
}
|
||||
}
|
||||
306
bfv/keygen.go
Normal file
306
bfv/keygen.go
Normal file
@@ -0,0 +1,306 @@
|
||||
package bfv
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
"math"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
type SecretKey struct {
|
||||
sk *ring.Poly
|
||||
}
|
||||
|
||||
type PublicKey struct {
|
||||
pk [2]*ring.Poly
|
||||
}
|
||||
|
||||
type KeyGenerator struct {
|
||||
bfvcontext *BfvContext
|
||||
context *ring.Context
|
||||
polypool *ring.Poly
|
||||
}
|
||||
|
||||
type RotationKeys struct {
|
||||
bfvcontext *BfvContext
|
||||
bitDecomp uint64
|
||||
evakey_rot_col_L map[uint64]*SwitchingKey
|
||||
evakey_rot_col_R map[uint64]*SwitchingKey
|
||||
evakey_rot_row *SwitchingKey
|
||||
}
|
||||
|
||||
type EvaluationKey struct {
|
||||
evakey []*SwitchingKey
|
||||
}
|
||||
|
||||
type SwitchingKey struct {
|
||||
bitDecomp uint64
|
||||
evakey [][][2]*ring.Poly
|
||||
}
|
||||
|
||||
func (bfvcontext *BfvContext) NewKeyGenerator() (keygen *KeyGenerator) {
|
||||
keygen = new(KeyGenerator)
|
||||
keygen.bfvcontext = bfvcontext
|
||||
keygen.context = bfvcontext.contextQ
|
||||
keygen.polypool = keygen.context.NewPoly()
|
||||
return
|
||||
}
|
||||
|
||||
func (keygen *KeyGenerator) NewSecretKey() *SecretKey {
|
||||
|
||||
sk := new(SecretKey)
|
||||
sk.sk = keygen.bfvcontext.ternarySampler.SampleMontgomeryNTTNew()
|
||||
|
||||
return sk
|
||||
}
|
||||
|
||||
func (sk *SecretKey) Get() *ring.Poly {
|
||||
return sk.sk
|
||||
}
|
||||
|
||||
func (sk *SecretKey) Set(poly *ring.Poly) {
|
||||
sk.sk = poly.CopyNew()
|
||||
}
|
||||
|
||||
func (keygen *KeyGenerator) check_sk(sk_output *SecretKey) error {
|
||||
|
||||
if sk_output.Get().GetDegree() != int(keygen.context.N) {
|
||||
return errors.New("error : pol degree sk != bfvcontext.n")
|
||||
}
|
||||
|
||||
if len(sk_output.Get().Coeffs) != len(keygen.context.Modulus) {
|
||||
return errors.New("error : nb modulus sk != nb modulus bfvcontext")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (keygen *KeyGenerator) NewPublicKey(sk *SecretKey) (*PublicKey, error) {
|
||||
|
||||
if err := keygen.check_sk(sk); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pk := new(PublicKey)
|
||||
|
||||
//pk[0] = [-(a*s + e)]
|
||||
//pk[1] = [a]
|
||||
pk.pk[0] = keygen.bfvcontext.gaussianSampler.SampleNTTNew()
|
||||
pk.pk[1] = keygen.context.NewUniformPoly()
|
||||
|
||||
keygen.context.MulCoeffsMontgomeryAndAdd(sk.sk, pk.pk[1], pk.pk[0])
|
||||
keygen.context.Neg(pk.pk[0], pk.pk[0])
|
||||
|
||||
return pk, nil
|
||||
}
|
||||
|
||||
func (pk *PublicKey) Get() [2]*ring.Poly {
|
||||
return pk.pk
|
||||
}
|
||||
|
||||
func (pk *PublicKey) Set(p [2]*ring.Poly) {
|
||||
pk.pk[0] = p[0].CopyNew()
|
||||
pk.pk[1] = p[1].CopyNew()
|
||||
}
|
||||
|
||||
func (keygen *KeyGenerator) SetPublicKey(p [2]*ring.Poly) (*PublicKey, error) {
|
||||
|
||||
pk := new(PublicKey)
|
||||
|
||||
pk.pk[0] = p[0].CopyNew()
|
||||
pk.pk[1] = p[1].CopyNew()
|
||||
|
||||
return pk, nil
|
||||
}
|
||||
|
||||
func (keygen *KeyGenerator) NewKeyPair() (sk *SecretKey, pk *PublicKey, err error) {
|
||||
sk = keygen.NewSecretKey()
|
||||
pk, err = keygen.NewPublicKey(sk)
|
||||
return
|
||||
}
|
||||
|
||||
func (keygen *KeyGenerator) NewRelinKey(sk *SecretKey, maxDegree, bitDecomp uint64) (newEvakey *EvaluationKey, err error) {
|
||||
|
||||
newEvakey = new(EvaluationKey)
|
||||
newEvakey.evakey = make([]*SwitchingKey, maxDegree)
|
||||
sk.Get().Copy(keygen.polypool)
|
||||
for i := uint64(0); i < maxDegree; i++ {
|
||||
keygen.context.MulCoeffsMontgomery(keygen.polypool, sk.Get(), keygen.polypool)
|
||||
newEvakey.evakey[i] = newswitchingkey(keygen.bfvcontext, keygen.polypool, sk.Get(), bitDecomp)
|
||||
}
|
||||
keygen.polypool.Zero()
|
||||
|
||||
return newEvakey, nil
|
||||
}
|
||||
|
||||
func (evk *EvaluationKey) Get() []*SwitchingKey {
|
||||
return evk.evakey
|
||||
}
|
||||
|
||||
func (keygen *KeyGenerator) SetRelinKeys(rlk [][][][2]*ring.Poly, bitDecomp uint64) (*EvaluationKey, error) {
|
||||
|
||||
newevakey := new(EvaluationKey)
|
||||
|
||||
newevakey.evakey = make([]*SwitchingKey, len(rlk))
|
||||
for i := range rlk {
|
||||
newevakey.evakey[i] = new(SwitchingKey)
|
||||
newevakey.evakey[i].bitDecomp = bitDecomp
|
||||
newevakey.evakey[i].evakey = make([][][2]*ring.Poly, len(rlk[i]))
|
||||
for j := range rlk[i] {
|
||||
newevakey.evakey[i].evakey[j] = make([][2]*ring.Poly, len(rlk[i][j]))
|
||||
for u := range rlk[i][j] {
|
||||
newevakey.evakey[i].evakey[j][u][0] = rlk[i][j][u][0].CopyNew()
|
||||
newevakey.evakey[i].evakey[j][u][1] = rlk[i][j][u][1].CopyNew()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newevakey, nil
|
||||
}
|
||||
|
||||
func (keygen *KeyGenerator) NewSwitchingKey(sk_input, sk_output *SecretKey, bitDecomp uint64) (newevakey *SwitchingKey, err error) {
|
||||
|
||||
if err = keygen.check_sk(sk_input); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = keygen.check_sk(sk_output); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keygen.context.Sub(sk_input.Get(), sk_output.Get(), keygen.polypool)
|
||||
newevakey = newswitchingkey(keygen.bfvcontext, keygen.polypool, sk_output.Get(), bitDecomp)
|
||||
keygen.polypool.Zero()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (keygen *KeyGenerator) NewRotationKeys(sk_output *SecretKey, bitDecomp uint64, rotLeft []uint64, rotRight []uint64, conjugate bool) (rotKey *RotationKeys, err error) {
|
||||
|
||||
if err = keygen.check_sk(sk_output); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if bitDecomp > keygen.bfvcontext.maxBit || bitDecomp == 0 {
|
||||
bitDecomp = keygen.bfvcontext.maxBit
|
||||
}
|
||||
|
||||
rotKey = new(RotationKeys)
|
||||
rotKey.bfvcontext = keygen.bfvcontext
|
||||
rotKey.bitDecomp = bitDecomp
|
||||
|
||||
if rotLeft != nil {
|
||||
rotKey.evakey_rot_col_L = make(map[uint64]*SwitchingKey)
|
||||
for _, n := range rotLeft {
|
||||
if rotKey.evakey_rot_col_L[n] == nil && n != 0 {
|
||||
rotKey.evakey_rot_col_L[n] = genrotkey(keygen, sk_output.Get(), keygen.bfvcontext.galElRotColLeft[n], bitDecomp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if rotRight != nil {
|
||||
rotKey.evakey_rot_col_R = make(map[uint64]*SwitchingKey)
|
||||
for _, n := range rotRight {
|
||||
if rotKey.evakey_rot_col_R[n] == nil && n != 0 {
|
||||
rotKey.evakey_rot_col_R[n] = genrotkey(keygen, sk_output.Get(), keygen.bfvcontext.galElRotColRight[n], bitDecomp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if conjugate {
|
||||
rotKey.evakey_rot_row = genrotkey(keygen, sk_output.Get(), keygen.bfvcontext.galElRotRow, bitDecomp)
|
||||
}
|
||||
|
||||
return rotKey, nil
|
||||
|
||||
}
|
||||
|
||||
func (keygen *KeyGenerator) NewRotationKeysPow2(sk_output *SecretKey, bitDecomp uint64, conjugate bool) (rotKey *RotationKeys, err error) {
|
||||
|
||||
if err = keygen.check_sk(sk_output); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if bitDecomp > keygen.bfvcontext.maxBit || bitDecomp == 0 {
|
||||
bitDecomp = keygen.bfvcontext.maxBit
|
||||
}
|
||||
|
||||
rotKey = new(RotationKeys)
|
||||
rotKey.bfvcontext = keygen.bfvcontext
|
||||
rotKey.bitDecomp = bitDecomp
|
||||
|
||||
rotKey.evakey_rot_col_L = make(map[uint64]*SwitchingKey)
|
||||
rotKey.evakey_rot_col_R = make(map[uint64]*SwitchingKey)
|
||||
|
||||
for n := uint64(1); n < rotKey.bfvcontext.n>>1; n <<= 1 {
|
||||
|
||||
rotKey.evakey_rot_col_L[n] = genrotkey(keygen, sk_output.Get(), keygen.bfvcontext.galElRotColLeft[n], bitDecomp)
|
||||
rotKey.evakey_rot_col_R[n] = genrotkey(keygen, sk_output.Get(), keygen.bfvcontext.galElRotColRight[n], bitDecomp)
|
||||
}
|
||||
|
||||
if conjugate {
|
||||
rotKey.evakey_rot_row = genrotkey(keygen, sk_output.Get(), keygen.bfvcontext.galElRotRow, bitDecomp)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func genrotkey(keygen *KeyGenerator, sk_output *ring.Poly, gen, bitDecomp uint64) (switchingkey *SwitchingKey) {
|
||||
|
||||
ring.PermuteNTT(sk_output, gen, keygen.polypool)
|
||||
keygen.context.Sub(keygen.polypool, sk_output, keygen.polypool)
|
||||
switchingkey = newswitchingkey(keygen.bfvcontext, keygen.polypool, sk_output, bitDecomp)
|
||||
keygen.polypool.Zero()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func newswitchingkey(bfvcontext *BfvContext, sk_in, sk_out *ring.Poly, bitDecomp uint64) (switchingkey *SwitchingKey) {
|
||||
|
||||
if bitDecomp > bfvcontext.maxBit || bitDecomp == 0 {
|
||||
bitDecomp = bfvcontext.maxBit
|
||||
}
|
||||
|
||||
switchingkey = new(SwitchingKey)
|
||||
|
||||
context := bfvcontext.contextQ
|
||||
|
||||
switchingkey.bitDecomp = uint64(bitDecomp)
|
||||
|
||||
mredParams := context.GetMredParams()
|
||||
|
||||
// delta_sk = sk_input - sk_output = GaloisEnd(sk_output, rotation) - sk_output
|
||||
|
||||
var bitLog uint64
|
||||
|
||||
switchingkey.evakey = make([][][2]*ring.Poly, len(context.Modulus))
|
||||
|
||||
for i, qi := range context.Modulus {
|
||||
|
||||
bitLog = uint64(math.Ceil(float64(bits.Len64(qi)) / float64(bitDecomp)))
|
||||
|
||||
switchingkey.evakey[i] = make([][2]*ring.Poly, bitLog)
|
||||
|
||||
for j := uint64(0); j < bitLog; j++ {
|
||||
|
||||
// e
|
||||
switchingkey.evakey[i][j][0] = bfvcontext.gaussianSampler.SampleNTTNew()
|
||||
// a
|
||||
switchingkey.evakey[i][j][1] = context.NewUniformPoly()
|
||||
|
||||
// e + sk_in * (qiBarre*qiStar) * 2^w
|
||||
// (qiBarre*qiStar)%qi = 1, else 0
|
||||
for w := uint64(0); w < context.N; w++ {
|
||||
switchingkey.evakey[i][j][0].Coeffs[i][w] += PowerOf2(sk_in.Coeffs[i][w], bitDecomp*j, qi, mredParams[i])
|
||||
}
|
||||
|
||||
// sk_in * (qiBarre*qiStar) * 2^w - a*sk + e
|
||||
context.MulCoeffsMontgomeryAndSub(switchingkey.evakey[i][j][1], sk_out, switchingkey.evakey[i][j][0])
|
||||
|
||||
context.MForm(switchingkey.evakey[i][j][0], switchingkey.evakey[i][j][0])
|
||||
context.MForm(switchingkey.evakey[i][j][1], switchingkey.evakey[i][j][1])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
651
bfv/marshaler.go
Normal file
651
bfv/marshaler.go
Normal file
@@ -0,0 +1,651 @@
|
||||
package bfv
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
"math"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
func (bfvContext *BfvContext) MarshalBinary() ([]byte, error) {
|
||||
|
||||
N := bfvContext.n
|
||||
numberModuliesQ := len(bfvContext.contextQ.Modulus)
|
||||
numberModuliesP := len(bfvContext.contextP.Modulus)
|
||||
|
||||
if numberModuliesQ > 0xFF {
|
||||
return nil, errors.New("error : bfvcontext numberModuliesQ overflow uint8")
|
||||
}
|
||||
|
||||
if numberModuliesP > 0xFF {
|
||||
return nil, errors.New("error : bfvcontext numberModuliesP overflow uint8")
|
||||
}
|
||||
|
||||
data := make([]byte, 3+((2+numberModuliesQ+numberModuliesP)<<3))
|
||||
|
||||
data[0] = uint8(bits.Len64(N) - 1)
|
||||
data[1] = uint8(numberModuliesQ)
|
||||
data[2] = uint8(numberModuliesP)
|
||||
|
||||
pointer := 3
|
||||
|
||||
binary.BigEndian.PutUint64(data[pointer:pointer+8], uint64(bfvContext.sigma*(1<<32)))
|
||||
pointer += 8
|
||||
|
||||
binary.BigEndian.PutUint64(data[pointer:pointer+8], bfvContext.contextT.Modulus[0])
|
||||
pointer += 8
|
||||
|
||||
for i := 0; i < numberModuliesQ; i++ {
|
||||
binary.BigEndian.PutUint64(data[pointer:pointer+8], bfvContext.contextQ.Modulus[i])
|
||||
pointer += 8
|
||||
}
|
||||
|
||||
for i := 0; i < numberModuliesP; i++ {
|
||||
binary.BigEndian.PutUint64(data[pointer:pointer+8], bfvContext.contextP.Modulus[i])
|
||||
pointer += 8
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (bfvContext *BfvContext) UnMarshalBinary(data []byte) error {
|
||||
//var err error
|
||||
|
||||
N := uint64(1 << data[0])
|
||||
numberModuliesQ := int(data[1])
|
||||
numberModuliesP := int(data[2])
|
||||
|
||||
pointer := 3
|
||||
|
||||
if ((len(data) - pointer) >> 3) != (2 + numberModuliesQ + numberModuliesP) {
|
||||
return errors.New("error : invalid PublicKey encoding")
|
||||
}
|
||||
|
||||
sigma := math.Round((float64(binary.BigEndian.Uint64(data[pointer:pointer+8]))/float64(1<<32))*100) / 100
|
||||
pointer += 8
|
||||
|
||||
t := binary.BigEndian.Uint64(data[pointer : pointer+8])
|
||||
pointer += 8
|
||||
|
||||
ModuliesQ := make([]uint64, numberModuliesQ)
|
||||
for i := 0; i < numberModuliesQ; i++ {
|
||||
ModuliesQ[i] = binary.BigEndian.Uint64(data[pointer : pointer+8])
|
||||
pointer += 8
|
||||
}
|
||||
|
||||
ModuliesP := make([]uint64, numberModuliesP)
|
||||
for i := 0; i < numberModuliesP; i++ {
|
||||
ModuliesP[i] = binary.BigEndian.Uint64(data[pointer : pointer+8])
|
||||
pointer += 8
|
||||
}
|
||||
|
||||
bfvContext.SetParameters(N, t, ModuliesQ, ModuliesP, sigma)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (P *Plaintext) MarshalBinary() ([]byte, error) {
|
||||
|
||||
var err error
|
||||
|
||||
N := uint64(len(P.value[0].Coeffs[0]))
|
||||
|
||||
data := make([]byte, 1+(N<<3))
|
||||
|
||||
data[0] = uint8(bits.Len64(uint64(N)) - 1)
|
||||
|
||||
pointer := uint64(1)
|
||||
|
||||
if _, err = ring.WriteCoeffsTo(pointer, N, 1, P.value[0].Coeffs, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (P *Plaintext) UnMarshalBinary(data []byte) error {
|
||||
|
||||
N := uint64(1 << data[0])
|
||||
|
||||
P.value = make([]*ring.Poly, 1)
|
||||
P.value[0] = new(ring.Poly)
|
||||
P.value[0].Coeffs = make([][]uint64, 1)
|
||||
P.value[0].Coeffs[0] = make([]uint64, N)
|
||||
|
||||
pointer := uint64(1)
|
||||
|
||||
if ((uint64(len(data)) - pointer) >> 3) != N {
|
||||
return errors.New("error : invalid Plaintext encoding")
|
||||
}
|
||||
|
||||
pointer, _ = ring.DecodeCoeffs(pointer, N, 1, P.value[0].Coeffs, data)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ciphertext *Ciphertext) MarshalBinary() ([]byte, error) {
|
||||
|
||||
var err error
|
||||
|
||||
N := uint64(len(ciphertext.Value()[0].Coeffs[0]))
|
||||
numberModulies := uint64(len(ciphertext.Value()[0].Coeffs))
|
||||
degree := ciphertext.Degree()
|
||||
|
||||
if numberModulies > 0xFF {
|
||||
return nil, errors.New("error : ciphertext numberModulies overflow uint8")
|
||||
}
|
||||
|
||||
if degree > 0xFF {
|
||||
return nil, errors.New("error : ciphertext degree overflow uint8")
|
||||
}
|
||||
|
||||
data := make([]byte, 3+((N*numberModulies*(degree+1))<<3))
|
||||
|
||||
data[0] = uint8(bits.Len64(uint64(N)) - 1)
|
||||
data[1] = uint8(numberModulies)
|
||||
data[2] = uint8(degree)
|
||||
|
||||
pointer := uint64(3)
|
||||
|
||||
for i := uint64(0); i < degree+1; i++ {
|
||||
if pointer, err = ring.WriteCoeffsTo(pointer, N, numberModulies, ciphertext.Value()[i].Coeffs, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (ciphertext *Ciphertext) UnmarshalBinary(data []byte) error {
|
||||
|
||||
N := uint64(1 << data[0])
|
||||
numberModulies := uint64(data[1])
|
||||
degree := uint64(data[2])
|
||||
|
||||
ciphertext.SetValue(make([]*ring.Poly, degree+1))
|
||||
|
||||
for i := uint64(0); i < degree+1; i++ {
|
||||
ciphertext.Value()[i] = new(ring.Poly)
|
||||
ciphertext.Value()[i].Coeffs = make([][]uint64, numberModulies)
|
||||
}
|
||||
|
||||
pointer := uint64(3)
|
||||
|
||||
if ((uint64(len(data)) - pointer) >> 3) != N*numberModulies*(degree+1) {
|
||||
return errors.New("error : invalid ciphertext encoding")
|
||||
}
|
||||
|
||||
for x := uint64(0); x < degree+1; x++ {
|
||||
pointer, _ = ring.DecodeCoeffs(pointer, N, numberModulies, ciphertext.Value()[x].Coeffs, data)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sk *SecretKey) MarshalBinary() ([]byte, error) {
|
||||
var err error
|
||||
|
||||
N := uint64(len(sk.sk.Coeffs[0]))
|
||||
numberModulies := uint64(len(sk.sk.Coeffs))
|
||||
|
||||
if numberModulies > 0xFF {
|
||||
return nil, errors.New("error : max degree uint16 overflow")
|
||||
}
|
||||
|
||||
data := make([]byte, 2+((N*numberModulies)<<3))
|
||||
|
||||
data[0] = uint8(bits.Len64(uint64(N)) - 1)
|
||||
data[1] = uint8(numberModulies)
|
||||
|
||||
pointer := uint64(2)
|
||||
|
||||
if _, err = ring.WriteCoeffsTo(pointer, N, numberModulies, sk.sk.Coeffs, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (sk *SecretKey) UnMarshalBinary(data []byte) error {
|
||||
|
||||
N := uint64(1 << data[0])
|
||||
numberModulies := uint64(data[1])
|
||||
|
||||
sk.sk = new(ring.Poly)
|
||||
sk.sk.Coeffs = make([][]uint64, numberModulies)
|
||||
|
||||
pointer := uint64(2)
|
||||
|
||||
if ((uint64(len(data)) - pointer) >> 3) != (N * numberModulies) {
|
||||
return errors.New("error : invalid SecretKey encoding")
|
||||
}
|
||||
|
||||
_, _ = ring.DecodeCoeffs(pointer, N, numberModulies, sk.sk.Coeffs, data)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PK
|
||||
func (pk *PublicKey) MarshalBinary() ([]byte, error) {
|
||||
|
||||
var err error
|
||||
|
||||
N := uint64(len(pk.pk[0].Coeffs[0]))
|
||||
numberModulies := uint64(len(pk.pk[0].Coeffs))
|
||||
|
||||
if numberModulies > 0xFF {
|
||||
return nil, errors.New("error : max degree uint16 overflow")
|
||||
}
|
||||
|
||||
data := make([]byte, 2+((N*numberModulies)<<4))
|
||||
|
||||
data[0] = uint8((bits.Len64(uint64(N)) - 1))
|
||||
data[1] = uint8(numberModulies)
|
||||
|
||||
pointer := uint64(2)
|
||||
|
||||
if pointer, err = ring.WriteCoeffsTo(pointer, N, numberModulies, pk.pk[0].Coeffs, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pointer, err = ring.WriteCoeffsTo(pointer, N, numberModulies, pk.pk[1].Coeffs, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (pk *PublicKey) UnMarshalBinary(data []byte) error {
|
||||
|
||||
N := uint64(1 << data[0])
|
||||
numberModulies := uint64(data[1])
|
||||
|
||||
pk.pk[0] = new(ring.Poly)
|
||||
pk.pk[0].Coeffs = make([][]uint64, numberModulies)
|
||||
pk.pk[1] = new(ring.Poly)
|
||||
pk.pk[1].Coeffs = make([][]uint64, numberModulies)
|
||||
|
||||
pointer := uint64(2)
|
||||
|
||||
if ((uint64(len(data)) - pointer) >> 4) != (N * numberModulies) {
|
||||
return errors.New("error : invalid PublicKey encoding")
|
||||
}
|
||||
|
||||
pointer, _ = ring.DecodeCoeffs(pointer, N, numberModulies, pk.pk[0].Coeffs, data)
|
||||
pointer, _ = ring.DecodeCoeffs(pointer, N, numberModulies, pk.pk[1].Coeffs, data)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (evaluationkey *EvaluationKey) MarshalBinary() ([]byte, error) {
|
||||
|
||||
var err error
|
||||
|
||||
N := uint64(len(evaluationkey.evakey[0].evakey[0][0][0].Coeffs[0]))
|
||||
numberModulies := uint64(len(evaluationkey.evakey[0].evakey[0][0][0].Coeffs))
|
||||
decomposition := numberModulies
|
||||
bitDecomp := evaluationkey.evakey[0].bitDecomp
|
||||
|
||||
maxDegree := uint64(len(evaluationkey.evakey))
|
||||
|
||||
if numberModulies > 0xFF {
|
||||
return nil, errors.New("error : max number modulies uint16 overflow")
|
||||
}
|
||||
|
||||
if decomposition > 0xFF {
|
||||
return nil, errors.New("error : max decomposition uint16 overflow")
|
||||
}
|
||||
|
||||
if bitDecomp > 0xFF {
|
||||
return nil, errors.New("error : max bitDecomp uint16 overflow")
|
||||
}
|
||||
|
||||
if maxDegree > 0xFF {
|
||||
return nil, errors.New("error : max degree uint16 overflow")
|
||||
}
|
||||
|
||||
var dataLen uint64
|
||||
dataLen = 5
|
||||
for i := uint64(0); i < maxDegree; i++ {
|
||||
for j := uint64(0); j < decomposition; j++ {
|
||||
dataLen += 1 //Information about the size of the bitdecomposition
|
||||
dataLen += 2 * 8 * N * numberModulies * decomposition * maxDegree * uint64(len(evaluationkey.evakey[i].evakey[j])) // nb coefficients * 8
|
||||
}
|
||||
}
|
||||
|
||||
data := make([]byte, dataLen)
|
||||
|
||||
data[0] = uint8(bits.Len64(uint64(N)) - 1)
|
||||
data[1] = uint8(numberModulies)
|
||||
data[2] = uint8(decomposition)
|
||||
data[3] = uint8(bitDecomp)
|
||||
data[4] = uint8(maxDegree)
|
||||
|
||||
pointer := uint64(5)
|
||||
|
||||
var bitLog uint8
|
||||
for i := uint64(0); i < maxDegree; i++ {
|
||||
for j := uint64(0); j < decomposition; j++ {
|
||||
bitLog = uint8(len(evaluationkey.evakey[i].evakey[j]))
|
||||
data[pointer] = bitLog
|
||||
pointer += 1
|
||||
for x := uint8(0); x < bitLog; x++ {
|
||||
if pointer, err = ring.WriteCoeffsTo(pointer, N, numberModulies, evaluationkey.evakey[i].evakey[j][x][0].Coeffs, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pointer, err = ring.WriteCoeffsTo(pointer, N, numberModulies, evaluationkey.evakey[i].evakey[j][x][1].Coeffs, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (evaluationkey *EvaluationKey) UnMarshalBinary(data []byte) error {
|
||||
|
||||
N := uint64(1 << data[0])
|
||||
numberModulies := uint64(data[1])
|
||||
decomposition := uint64(data[2])
|
||||
bitDecomp := uint64(data[3])
|
||||
maxDegree := uint64(data[4])
|
||||
|
||||
evaluationkey.evakey = make([]*SwitchingKey, maxDegree)
|
||||
|
||||
pointer := uint64(5)
|
||||
var bitLog uint64
|
||||
for i := uint64(0); i < maxDegree; i++ {
|
||||
|
||||
evaluationkey.evakey[i] = new(SwitchingKey)
|
||||
evaluationkey.evakey[i].bitDecomp = bitDecomp
|
||||
evaluationkey.evakey[i].evakey = make([][][2]*ring.Poly, decomposition)
|
||||
|
||||
for j := uint64(0); j < decomposition; j++ {
|
||||
|
||||
bitLog = uint64(data[pointer])
|
||||
pointer += 1
|
||||
|
||||
evaluationkey.evakey[i].evakey[j] = make([][2]*ring.Poly, bitLog)
|
||||
|
||||
for x := uint64(0); x < bitLog; x++ {
|
||||
|
||||
evaluationkey.evakey[i].evakey[j][x][0] = new(ring.Poly)
|
||||
evaluationkey.evakey[i].evakey[j][x][0].Coeffs = make([][]uint64, numberModulies)
|
||||
pointer, _ = ring.DecodeCoeffs(pointer, N, numberModulies, evaluationkey.evakey[i].evakey[j][x][0].Coeffs, data)
|
||||
|
||||
evaluationkey.evakey[i].evakey[j][x][1] = new(ring.Poly)
|
||||
evaluationkey.evakey[i].evakey[j][x][1].Coeffs = make([][]uint64, numberModulies)
|
||||
pointer, _ = ring.DecodeCoeffs(pointer, N, numberModulies, evaluationkey.evakey[i].evakey[j][x][1].Coeffs, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rotationkey *RotationKeys) MarshalBinary() ([]byte, error) {
|
||||
|
||||
var err error
|
||||
|
||||
N := uint64(rotationkey.bfvcontext.n)
|
||||
numberModulies := uint64(len(rotationkey.bfvcontext.contextQ.Modulus))
|
||||
decomposition := numberModulies
|
||||
bitDecomp := rotationkey.bitDecomp
|
||||
mappingRow := 0
|
||||
mappingColL := []uint64{}
|
||||
mappingColR := []uint64{}
|
||||
|
||||
if numberModulies > 0xFF {
|
||||
return nil, errors.New("error : max number modulies uint16 overflow")
|
||||
}
|
||||
|
||||
if decomposition > 0xFF {
|
||||
return nil, errors.New("error : max decomposition uint16 overflow")
|
||||
}
|
||||
|
||||
if bitDecomp > 0xFF {
|
||||
return nil, errors.New("error : max bitDecomp uint16 overflow")
|
||||
}
|
||||
|
||||
var dataLen uint64
|
||||
dataLen = 13
|
||||
|
||||
for i := uint64(1); i < rotationkey.bfvcontext.n>>1; i++ {
|
||||
if rotationkey.evakey_rot_col_L[i] != nil {
|
||||
|
||||
mappingColL = append(mappingColL, i)
|
||||
|
||||
for j := uint64(0); j < decomposition; j++ {
|
||||
dataLen += 1 //Information about the size of the bitdecomposition
|
||||
dataLen += 2 * 8 * N * numberModulies * decomposition * uint64(len(rotationkey.evakey_rot_col_L[i].evakey[j])) // nb coefficients * 8
|
||||
}
|
||||
}
|
||||
|
||||
if rotationkey.evakey_rot_col_L[i] != nil {
|
||||
|
||||
mappingColR = append(mappingColR, i)
|
||||
|
||||
for j := uint64(0); j < decomposition; j++ {
|
||||
dataLen += 1 //Information about the size of the bitdecomposition
|
||||
dataLen += 2 * 8 * N * numberModulies * decomposition * uint64(len(rotationkey.evakey_rot_col_L[i].evakey[j])) // nb coefficients * 8
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if rotationkey.evakey_rot_row != nil {
|
||||
mappingRow = 1
|
||||
for j := uint64(0); j < decomposition; j++ {
|
||||
dataLen += 1 //Information about the size of the bitdecomposition
|
||||
dataLen += 2 * 8 * N * numberModulies * decomposition * uint64(len(rotationkey.evakey_rot_row.evakey[j])) // nb coefficients * 8
|
||||
}
|
||||
}
|
||||
|
||||
dataLen += uint64(len(mappingColL)+len(mappingColR)) << 2 // size needed to encode what rotation are present
|
||||
|
||||
data := make([]byte, dataLen)
|
||||
|
||||
data[0] = uint8(bits.Len64(uint64(N)) - 1)
|
||||
data[1] = uint8(numberModulies)
|
||||
data[2] = uint8(decomposition)
|
||||
data[3] = uint8(bitDecomp)
|
||||
data[4] = uint8(mappingRow)
|
||||
|
||||
pointer := uint64(5)
|
||||
|
||||
binary.BigEndian.PutUint32(data[pointer:pointer+4], uint32(len(mappingColL)))
|
||||
pointer += 4
|
||||
|
||||
binary.BigEndian.PutUint32(data[pointer:pointer+4], uint32(len(mappingColR)))
|
||||
pointer += 4
|
||||
|
||||
for _, i := range mappingColL {
|
||||
|
||||
binary.BigEndian.PutUint32(data[pointer:pointer+4], uint32(i))
|
||||
|
||||
pointer += 4
|
||||
}
|
||||
|
||||
for _, i := range mappingColR {
|
||||
|
||||
binary.BigEndian.PutUint32(data[pointer:pointer+4], uint32(i))
|
||||
|
||||
pointer += 4
|
||||
}
|
||||
|
||||
// Encodes the different rotation key indexes
|
||||
var bitLog uint8
|
||||
if mappingRow == 1 {
|
||||
for j := uint64(0); j < decomposition; j++ {
|
||||
bitLog = uint8(len(rotationkey.evakey_rot_row.evakey[j]))
|
||||
data[pointer] = bitLog
|
||||
pointer += 1
|
||||
for x := uint8(0); x < bitLog; x++ {
|
||||
if pointer, err = ring.WriteCoeffsTo(pointer, N, numberModulies, rotationkey.evakey_rot_row.evakey[j][x][0].Coeffs, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pointer, err = ring.WriteCoeffsTo(pointer, N, numberModulies, rotationkey.evakey_rot_row.evakey[j][x][1].Coeffs, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, i := range mappingColL {
|
||||
for j := uint64(0); j < decomposition; j++ {
|
||||
bitLog = uint8(len(rotationkey.evakey_rot_col_L[i].evakey[j]))
|
||||
data[pointer] = bitLog
|
||||
pointer += 1
|
||||
for x := uint8(0); x < bitLog; x++ {
|
||||
if pointer, err = ring.WriteCoeffsTo(pointer, N, numberModulies, rotationkey.evakey_rot_col_L[i].evakey[j][x][0].Coeffs, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pointer, err = ring.WriteCoeffsTo(pointer, N, numberModulies, rotationkey.evakey_rot_col_L[i].evakey[j][x][1].Coeffs, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, i := range mappingColR {
|
||||
for j := uint64(0); j < decomposition; j++ {
|
||||
bitLog = uint8(len(rotationkey.evakey_rot_col_R[i].evakey[j]))
|
||||
data[pointer] = bitLog
|
||||
pointer += 1
|
||||
for x := uint8(0); x < bitLog; x++ {
|
||||
if pointer, err = ring.WriteCoeffsTo(pointer, N, numberModulies, rotationkey.evakey_rot_col_R[i].evakey[j][x][0].Coeffs, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pointer, err = ring.WriteCoeffsTo(pointer, N, numberModulies, rotationkey.evakey_rot_col_R[i].evakey[j][x][1].Coeffs, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (rotationkey *RotationKeys) UnMarshalBinary(data []byte) error {
|
||||
|
||||
N := uint64(1 << data[0])
|
||||
numberModulies := uint64(data[1])
|
||||
decomposition := uint64(data[2])
|
||||
bitDecomp := uint64(data[3])
|
||||
mappingRow := uint64(data[4])
|
||||
mappingColL := make([]uint64, binary.BigEndian.Uint32(data[5:9]))
|
||||
mappingColR := make([]uint64, binary.BigEndian.Uint32(data[9:13]))
|
||||
|
||||
rotationkey.bitDecomp = uint64(bitDecomp)
|
||||
|
||||
rotationkey.evakey_rot_col_L = make(map[uint64]*SwitchingKey)
|
||||
//rotationkey.evakey_rot_col_R = make(map[uint64][][][2]*ring.Poly)
|
||||
|
||||
pointer := uint64(13)
|
||||
|
||||
for i := 0; i < len(mappingColL); i++ {
|
||||
mappingColL[i] = uint64(binary.BigEndian.Uint32(data[pointer : pointer+4]))
|
||||
pointer += 4
|
||||
}
|
||||
|
||||
for i := 0; i < len(mappingColR); i++ {
|
||||
mappingColR[i] = uint64(binary.BigEndian.Uint32(data[pointer : pointer+4]))
|
||||
pointer += 4
|
||||
}
|
||||
|
||||
var bitLog uint64
|
||||
if mappingRow == 1 {
|
||||
|
||||
rotationkey.evakey_rot_row = new(SwitchingKey)
|
||||
rotationkey.evakey_rot_row.bitDecomp = bitDecomp
|
||||
rotationkey.evakey_rot_row.evakey = make([][][2]*ring.Poly, decomposition)
|
||||
|
||||
for j := uint64(0); j < decomposition; j++ {
|
||||
|
||||
bitLog = uint64(data[pointer])
|
||||
pointer += 1
|
||||
|
||||
rotationkey.evakey_rot_row.evakey[j] = make([][2]*ring.Poly, bitLog)
|
||||
|
||||
for x := uint64(0); x < bitLog; x++ {
|
||||
|
||||
rotationkey.evakey_rot_row.evakey[j][x][0] = new(ring.Poly)
|
||||
rotationkey.evakey_rot_row.evakey[j][x][0].Coeffs = make([][]uint64, numberModulies)
|
||||
pointer, _ = ring.DecodeCoeffs(pointer, N, numberModulies, rotationkey.evakey_rot_row.evakey[j][x][0].Coeffs, data)
|
||||
|
||||
rotationkey.evakey_rot_row.evakey[j][x][1] = new(ring.Poly)
|
||||
rotationkey.evakey_rot_row.evakey[j][x][1].Coeffs = make([][]uint64, numberModulies)
|
||||
pointer, _ = ring.DecodeCoeffs(pointer, N, numberModulies, rotationkey.evakey_rot_row.evakey[j][x][1].Coeffs, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(mappingColL) > 0 {
|
||||
|
||||
rotationkey.evakey_rot_col_L = make(map[uint64]*SwitchingKey)
|
||||
|
||||
for _, i := range mappingColL {
|
||||
|
||||
rotationkey.evakey_rot_col_L[i] = new(SwitchingKey)
|
||||
rotationkey.evakey_rot_col_L[i].bitDecomp = bitDecomp
|
||||
rotationkey.evakey_rot_col_L[i].evakey = make([][][2]*ring.Poly, decomposition)
|
||||
|
||||
for j := uint64(0); j < decomposition; j++ {
|
||||
|
||||
bitLog = uint64(data[pointer])
|
||||
pointer += 1
|
||||
|
||||
rotationkey.evakey_rot_col_L[i].evakey[j] = make([][2]*ring.Poly, bitLog)
|
||||
|
||||
for x := uint64(0); x < bitLog; x++ {
|
||||
|
||||
rotationkey.evakey_rot_col_L[i].evakey[j][x][0] = new(ring.Poly)
|
||||
rotationkey.evakey_rot_col_L[i].evakey[j][x][0].Coeffs = make([][]uint64, numberModulies)
|
||||
pointer, _ = ring.DecodeCoeffs(pointer, N, numberModulies, rotationkey.evakey_rot_col_L[i].evakey[j][x][0].Coeffs, data)
|
||||
|
||||
rotationkey.evakey_rot_col_L[i].evakey[j][x][1] = new(ring.Poly)
|
||||
rotationkey.evakey_rot_col_L[i].evakey[j][x][1].Coeffs = make([][]uint64, numberModulies)
|
||||
pointer, _ = ring.DecodeCoeffs(pointer, N, numberModulies, rotationkey.evakey_rot_col_L[i].evakey[j][x][1].Coeffs, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(mappingColR) > 0 {
|
||||
|
||||
rotationkey.evakey_rot_col_R = make(map[uint64]*SwitchingKey)
|
||||
|
||||
for _, i := range mappingColR {
|
||||
|
||||
rotationkey.evakey_rot_col_R[i] = new(SwitchingKey)
|
||||
rotationkey.evakey_rot_col_R[i].bitDecomp = bitDecomp
|
||||
rotationkey.evakey_rot_col_R[i].evakey = make([][][2]*ring.Poly, decomposition)
|
||||
|
||||
for j := uint64(0); j < decomposition; j++ {
|
||||
|
||||
bitLog = uint64(data[pointer])
|
||||
pointer += 1
|
||||
|
||||
rotationkey.evakey_rot_col_R[i].evakey[j] = make([][2]*ring.Poly, bitLog)
|
||||
|
||||
for x := uint64(0); x < bitLog; x++ {
|
||||
|
||||
rotationkey.evakey_rot_col_R[i].evakey[j][x][0] = new(ring.Poly)
|
||||
rotationkey.evakey_rot_col_R[i].evakey[j][x][0].Coeffs = make([][]uint64, numberModulies)
|
||||
pointer, _ = ring.DecodeCoeffs(pointer, N, numberModulies, rotationkey.evakey_rot_col_R[i].evakey[j][x][0].Coeffs, data)
|
||||
|
||||
rotationkey.evakey_rot_col_R[i].evakey[j][x][1] = new(ring.Poly)
|
||||
rotationkey.evakey_rot_col_R[i].evakey[j][x][1].Coeffs = make([][]uint64, numberModulies)
|
||||
pointer, _ = ring.DecodeCoeffs(pointer, N, numberModulies, rotationkey.evakey_rot_col_R[i].evakey[j][x][1].Coeffs, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
30
bfv/params.go
Normal file
30
bfv/params.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package bfv
|
||||
|
||||
import "github.com/lca1/lattigo/ring"
|
||||
|
||||
//https://projects.csail.mit.edu/HEWorkshop/HomomorphicEncryptionStandard2018.pdf
|
||||
|
||||
// Power of 2 plaintext modulus
|
||||
var Tpow2 = []uint64{2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, 2147483648, 4294967296}
|
||||
|
||||
// Plaintext modulus allowing batching for the corresponding N in ascending bitsize.
|
||||
var TBatching4096 = []uint64{40961, 114689, 188417, 417793, 1032193, 2056193, 4169729, 8380417, 16760833, 33538049, 67084289, 134176769, 268369921, 536813569, 1073692673, 2147377153, 4294828033}
|
||||
var TBatching8192 = []uint64{65537, 114689, 163841, 1032193, 1785857, 4079617, 8273921, 16760833, 33538049, 67043329, 133857281, 268369921, 536690689, 1073692673, 2147352577, 4294475777}
|
||||
var TBatching16384 = []uint64{65537, 163841, 786433, 1769473, 3735553, 8257537, 16580609, 33292289, 67043329, 133857281, 268369921, 536641537, 1073643521, 2147352577, 4294475777}
|
||||
var TBatching32768 = []uint64{65537, 786433, 1769473, 3735553, 8257537, 16580609, 33292289, 67043329, 132710401, 268369921, 536608769, 1073479681, 2147352577, 4293918721}
|
||||
|
||||
type Params struct {
|
||||
N uint64
|
||||
T uint64
|
||||
Qi []uint64
|
||||
Pi []uint64
|
||||
Sigma float64
|
||||
}
|
||||
|
||||
// ~128 bit security with sigma = 3.19 and secret with ternary distribution
|
||||
var ParamSets60 = []Params{
|
||||
{4096, 65537, ring.Qi60[len(ring.Qi60)-2:], ring.Pi60[len(ring.Pi60)-3:], 3.19},
|
||||
{8192, 65537, ring.Qi60[len(ring.Qi60)-4:], ring.Pi60[len(ring.Pi60)-5:], 3.19},
|
||||
{16384, 65537, ring.Qi60[len(ring.Qi60)-8:], ring.Pi60[len(ring.Pi60)-9:], 3.19},
|
||||
{32768, 65537, ring.Qi60[len(ring.Qi60)-16:], ring.Pi60[len(ring.Pi60)-17:], 3.19},
|
||||
}
|
||||
193
bfv/plaintext.go
Normal file
193
bfv/plaintext.go
Normal file
@@ -0,0 +1,193 @@
|
||||
package bfv
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
)
|
||||
|
||||
// The plaintext is a ring of N coefficients with two contexts.
|
||||
// The first context is defined by the BFV parameters. The second
|
||||
// context defines a NTT around its modulus it it permits it.
|
||||
|
||||
type Plaintext BigPoly
|
||||
|
||||
// NewCiphertext creates a new ciphertext
|
||||
func (bfvcontext *BfvContext) NewPlaintext() *Plaintext {
|
||||
|
||||
plaintext := new(Plaintext)
|
||||
|
||||
plaintext.bfvcontext = bfvcontext
|
||||
plaintext.value = []*ring.Poly{bfvcontext.contextQ.NewPoly()}
|
||||
plaintext.isNTT = false
|
||||
|
||||
return plaintext
|
||||
}
|
||||
|
||||
func (bfvcontext *BfvContext) NewRandomPlaintextCoeffs() (coeffs []uint64) {
|
||||
coeffs = make([]uint64, bfvcontext.n)
|
||||
for i := uint64(0); i < bfvcontext.n; i++ {
|
||||
coeffs[i] = ring.RandUniform(bfvcontext.t)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (P *Plaintext) SetCoefficientsInt64(coeffs []int64) {
|
||||
for i, coeff := range coeffs {
|
||||
for j := range P.bfvcontext.contextQ.Modulus {
|
||||
P.value[0].Coeffs[j][i] = uint64((coeff%int64(P.bfvcontext.t))+int64(P.bfvcontext.t)) % P.bfvcontext.t
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (P *Plaintext) SetCoefficientsUint64(coeffs []uint64) {
|
||||
|
||||
for i, coeff := range coeffs {
|
||||
for j := range P.bfvcontext.contextQ.Modulus {
|
||||
P.value[0].Coeffs[j][i] = coeff % P.bfvcontext.t
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (P *Plaintext) GetCoefficients() [][]uint64 {
|
||||
return P.value[0].GetCoefficients()
|
||||
}
|
||||
|
||||
func (P *Plaintext) BfvContext() *BfvContext {
|
||||
return P.bfvcontext
|
||||
}
|
||||
|
||||
func (P *Plaintext) Value() []*ring.Poly {
|
||||
return P.value
|
||||
}
|
||||
|
||||
func (P *Plaintext) SetValue(value []*ring.Poly) {
|
||||
P.value = value
|
||||
}
|
||||
|
||||
func (P *Plaintext) IsNTT() bool {
|
||||
return P.isNTT
|
||||
}
|
||||
|
||||
func (P *Plaintext) SetIsNTT(value bool) {
|
||||
P.isNTT = value
|
||||
}
|
||||
|
||||
func (P *Plaintext) Degree() uint64 {
|
||||
return uint64(len(P.value) - 1)
|
||||
}
|
||||
|
||||
func (P *Plaintext) Lift() {
|
||||
context := P.bfvcontext.contextQ
|
||||
for j := uint64(0); j < P.bfvcontext.n; j++ {
|
||||
for i := len(context.Modulus) - 1; i >= 0; i-- {
|
||||
P.value[0].Coeffs[i][j] = ring.MRed(P.value[0].Coeffs[0][j], P.bfvcontext.DeltaMont[i], context.Modulus[i], context.GetMredParams()[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (P *Plaintext) Resize(degree uint64) {
|
||||
if P.Degree() > degree {
|
||||
P.value = P.value[:degree]
|
||||
} else if P.Degree() < degree {
|
||||
for P.Degree() < degree {
|
||||
P.value = append(P.value, []*ring.Poly{P.bfvcontext.contextQ.NewPoly()}...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (P *Plaintext) Add(p0, p1 *Plaintext) {
|
||||
P.bfvcontext.contextT.Add(p0.value[0], p1.value[0], P.value[0])
|
||||
}
|
||||
|
||||
func (P *Plaintext) Sub(p0, p1 *Plaintext) {
|
||||
P.bfvcontext.contextT.Sub(p0.value[0], p1.value[0], P.value[0])
|
||||
}
|
||||
|
||||
func (P *Plaintext) Mul(p0, p1 *Plaintext) {
|
||||
|
||||
// Checks if the plaintext contexts has been validated (allowing NTT)
|
||||
// Else performe the multiplication with a naive convolution
|
||||
|
||||
if P.bfvcontext.contextT.IsValidated() {
|
||||
P.bfvcontext.contextT.MulPoly(p0.value[0], p1.value[0], P.value[0])
|
||||
} else {
|
||||
P.bfvcontext.contextT.MulPolyNaive(p0.value[0], p1.value[0], P.value[0])
|
||||
}
|
||||
}
|
||||
|
||||
func (P *Plaintext) NTT(p BfvElement) error {
|
||||
if P.Degree() != p.Degree() {
|
||||
return errors.New("error : receiver element invalide degree (does not match)")
|
||||
}
|
||||
if P.IsNTT() != true {
|
||||
for i := range P.value {
|
||||
P.bfvcontext.contextQ.NTT(P.Value()[i], p.Value()[i])
|
||||
}
|
||||
p.SetIsNTT(true)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (P *Plaintext) InvNTT(p BfvElement) error {
|
||||
if P.Degree() != p.Degree() {
|
||||
return errors.New("error : receiver element invalide degree (does not match)")
|
||||
}
|
||||
if P.IsNTT() != false {
|
||||
for i := range P.value {
|
||||
P.bfvcontext.contextQ.InvNTT(P.Value()[i], p.Value()[i])
|
||||
}
|
||||
p.SetIsNTT(false)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (P *Plaintext) EMBInv() error {
|
||||
|
||||
if P.bfvcontext.contextT.IsValidated() != true {
|
||||
return errors.New("plaintext context doesn't allow a valid NTT")
|
||||
}
|
||||
|
||||
P.bfvcontext.contextT.NTT(P.value[0], P.value[0])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (P *Plaintext) EMB() error {
|
||||
if P.bfvcontext.contextT.IsValidated() != true {
|
||||
return errors.New("plaintext context doesn't allow a valid InvNTT")
|
||||
}
|
||||
|
||||
P.bfvcontext.contextT.InvNTT(P.value[0], P.value[0])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Creates a new ciphertext of the same format and coefficients.
|
||||
func (P *Plaintext) CopyNew() BfvElement {
|
||||
|
||||
PCopy := new(Plaintext)
|
||||
|
||||
PCopy.value = make([]*ring.Poly, P.Degree()+1)
|
||||
for i := range P.value {
|
||||
PCopy.value[i] = P.value[i].CopyNew()
|
||||
}
|
||||
PCopy.bfvcontext = P.bfvcontext
|
||||
PCopy.isNTT = P.isNTT
|
||||
|
||||
return PCopy
|
||||
}
|
||||
|
||||
// Copies the value of the ciphertext on a reciever ciphertext of the same format
|
||||
func (P *Plaintext) Copy(PCopy BfvElement) error {
|
||||
|
||||
if !checkContext([]BfvElement{P, PCopy}) {
|
||||
return errors.New("input ciphertext are not using the same bfvcontext")
|
||||
}
|
||||
|
||||
for i := range P.value {
|
||||
PCopy.Value()[i].Copy(P.Value()[i])
|
||||
}
|
||||
P.SetIsNTT(P.IsNTT())
|
||||
|
||||
return nil
|
||||
}
|
||||
121
bfv/utils.go
Normal file
121
bfv/utils.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package bfv
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
"golang.org/x/crypto/blake2b"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
func Hash(data []uint64) (value []byte, err error) {
|
||||
hash, err := blake2b.New512(nil)
|
||||
buff := make([]byte, 8)
|
||||
for _, x := range data {
|
||||
binary.BigEndian.PutUint64(buff, x)
|
||||
hash.Write(buff)
|
||||
}
|
||||
value = hash.Sum(nil)
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func VerifyHash(hash0, hash1 []byte) bool {
|
||||
if res := bytes.Compare(hash0, hash1); res != 0 {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func checkContext(inputs []BfvElement) bool {
|
||||
|
||||
var value []byte
|
||||
|
||||
value = inputs[0].BfvContext().checksum
|
||||
|
||||
for i := range inputs[1:] {
|
||||
|
||||
if res := VerifyHash(value, inputs[i].BfvContext().checksum); res != true {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func EqualSlice(a, b []uint64) bool {
|
||||
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range a {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func min(values []uint64) (r uint64) {
|
||||
r = values[0]
|
||||
for _, i := range values[1:] {
|
||||
if i < r {
|
||||
r = i
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func max(values []uint64) (r uint64) {
|
||||
r = values[0]
|
||||
for _, i := range values[1:] {
|
||||
if i > r {
|
||||
r = i
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func bitReverse64(index, bitLen uint64) uint64 {
|
||||
indexReverse := uint64(0)
|
||||
for i := uint64(0); i < bitLen; i++ {
|
||||
if (index>>i)&1 != 0 {
|
||||
indexReverse |= 1 << (bitLen - 1 - i)
|
||||
}
|
||||
}
|
||||
return indexReverse
|
||||
}
|
||||
|
||||
func hammingWeight64(x uint64) uint64 {
|
||||
x -= (x >> 1) & 0x5555555555555555
|
||||
x = (x & 0x3333333333333333) + ((x >> 2) & 0x3333333333333333)
|
||||
x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0f
|
||||
return ((x * 0x0101010101010101) & 0xffffffffffffffff) >> 56
|
||||
}
|
||||
|
||||
func modexp(x, e, p uint64) (result uint64) {
|
||||
params := ring.BRedParams(p)
|
||||
result = 1
|
||||
for i := e; i > 0; i >>= 1 {
|
||||
if i&1 == 1 {
|
||||
result = ring.BRed(result, x, p, params)
|
||||
}
|
||||
x = ring.BRed(x, x, p, params)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Returns (x*2^n)%q where x is in montgomery form
|
||||
func PowerOf2(x, n, q, qInv uint64) (r uint64) {
|
||||
ahi, alo := x>>(64-n), x<<n
|
||||
R := alo * qInv
|
||||
H, _ := bits.Mul64(R, q)
|
||||
r = ahi - H + q
|
||||
if r >= q {
|
||||
r -= q
|
||||
}
|
||||
return
|
||||
}
|
||||
143
ckks/algorithms.go
Normal file
143
ckks/algorithms.go
Normal file
@@ -0,0 +1,143 @@
|
||||
package ckks
|
||||
|
||||
import (
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// SquareNew compute x^2 and returns the result on a new element. The input can be either a ciphertext or a plaintext.
|
||||
// In the case of a ciphertext input, an optional evaluation key given as input. If done so, a relinearization step will occure
|
||||
// after the squaring and the output ciphertext will remain at degree one. If not evaluation key is provided (nil),
|
||||
// the output ciphertext will be of degree two.
|
||||
func (evaluator *Evaluator) SquareNew(ct0 CkksElement, evakey *EvaluationKey) (ct1 CkksElement, err error) {
|
||||
ct1 = evaluator.ckkscontext.NewCiphertext(1, ct0.Level(), ct0.Scale())
|
||||
if err = evaluator.Square(ct0, evakey, ct1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ct1, nil
|
||||
}
|
||||
|
||||
// Square compute x^2 and returns the result on the receiver element. The input can be either a ciphertext or a plaintext.
|
||||
// In the case of a ciphertext input, an optional evaluation key given as input. If done so, a relinearization step will occure
|
||||
// after the squaring and the output ciphertext will remain at degree one. If not evaluation key is provided (nil),
|
||||
// the output ciphertext will be of degree two.
|
||||
func (evaluator *Evaluator) Square(ct0 CkksElement, evakey *EvaluationKey, ct1 CkksElement) error {
|
||||
|
||||
if err := evaluator.MulRelin(ct0, ct0, evakey, ct1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PowerOf2 compute x^(2^logPow2), consuming logPow2 levels, and returns the result on the receiver element. Providing an evaluation
|
||||
// key is necessary when logPow2 > 1.
|
||||
func (evaluator *Evaluator) PowerOf2(ct0 CkksElement, logPow2 uint64, evakey *EvaluationKey, ct1 CkksElement) error {
|
||||
|
||||
if logPow2 == 0 {
|
||||
|
||||
if ct0 != ct1 {
|
||||
|
||||
if err := ct0.Copy(ct1); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if err := evaluator.Square(ct0, evakey, ct1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
evaluator.Rescale(ct1, ct1)
|
||||
|
||||
for i := uint64(1); i < logPow2; i++ {
|
||||
if err := evaluator.Square(ct1, evakey, ct1); err != nil {
|
||||
return err
|
||||
}
|
||||
evaluator.Rescale(ct1, ct1)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Power compute x^degree, consuming log(degree) levels, and returns the result on a new element. Providing an evaluation
|
||||
// key is necessary when degree > 2.
|
||||
func (evaluator *Evaluator) PowerNew(ct0 *Ciphertext, degree uint64, evakey *EvaluationKey) (res *Ciphertext) {
|
||||
res = evaluator.ckkscontext.NewCiphertext(1, ct0.Level(), ct0.Scale())
|
||||
evaluator.Power(ct0, degree, evakey, res)
|
||||
return
|
||||
}
|
||||
|
||||
// Power compute x^degree, consuming log(degree) levels, and returns the result on the receiver element. Providing an evaluation
|
||||
// key is necessary when degree > 2.
|
||||
func (evaluator *Evaluator) Power(ct0 CkksElement, degree uint64, evakey *EvaluationKey, res CkksElement) error {
|
||||
|
||||
tmpct0 := ct0.CopyNew()
|
||||
|
||||
var logDegree, po2Degree uint64
|
||||
|
||||
logDegree = uint64(bits.Len64(degree)) - 1
|
||||
po2Degree = 1 << logDegree
|
||||
|
||||
if err := evaluator.PowerOf2(tmpct0, logDegree, evakey, res); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
degree -= po2Degree
|
||||
|
||||
for degree > 0 {
|
||||
|
||||
logDegree = uint64(bits.Len64(degree)) - 1
|
||||
po2Degree = 1 << logDegree
|
||||
|
||||
tmp := evaluator.ckkscontext.NewCiphertext(1, tmpct0.Level(), tmpct0.Scale())
|
||||
|
||||
if err := evaluator.PowerOf2(tmpct0, logDegree, evakey, tmp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := evaluator.MulRelin(res, tmp, evakey, res); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
evaluator.Rescale(res, res)
|
||||
|
||||
degree -= po2Degree
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// InverseNew computes 1/x, iterating for n steps and consuming n levels. The algorithm requirese x to be in the range
|
||||
// [-1.5 - 1.5i, 1.5 + 1.5i] or the result will be wrong. Each iteration increases the precision.
|
||||
func (evaluator *Evaluator) InverseNew(ct0 *Ciphertext, steps uint64, evakey *EvaluationKey) (res *Ciphertext, err error) {
|
||||
|
||||
cbar := evaluator.NegNew(ct0)
|
||||
|
||||
evaluator.AddConst(cbar, 1, cbar)
|
||||
|
||||
tmp := evaluator.AddConstNew(cbar, 1)
|
||||
res = tmp.CopyNew().(*Ciphertext)
|
||||
|
||||
for i := uint64(1); i < steps; i++ {
|
||||
|
||||
evaluator.Square(cbar, evakey, cbar)
|
||||
|
||||
if err = evaluator.Rescale(cbar, cbar); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tmp = evaluator.AddConstNew(cbar, 1)
|
||||
|
||||
if err := evaluator.MulRelin(tmp, res, evakey, tmp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
evaluator.Rescale(tmp, tmp)
|
||||
res = tmp.CopyNew().(*Ciphertext)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
34
ckks/bigpoly.go
Normal file
34
ckks/bigpoly.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package ckks
|
||||
|
||||
import (
|
||||
"github.com/lca1/lattigo/ring"
|
||||
)
|
||||
|
||||
type BigPoly struct {
|
||||
value []*ring.Poly
|
||||
ckkscontext *CkksContext
|
||||
scale uint64
|
||||
currentModulus *ring.Int
|
||||
isNTT bool
|
||||
isComplex bool
|
||||
}
|
||||
|
||||
type CkksElement interface {
|
||||
Value() []*ring.Poly
|
||||
SetValue([]*ring.Poly)
|
||||
CkksContext() *CkksContext
|
||||
Scale() uint64
|
||||
SetScale(uint64)
|
||||
CurrentModulus() *ring.Int
|
||||
SetCurrentModulus(*ring.Int)
|
||||
CopyParams(CkksElement)
|
||||
Resize(uint64)
|
||||
CopyNew() CkksElement
|
||||
Copy(CkksElement) error
|
||||
Degree() uint64
|
||||
Level() uint64
|
||||
NTT(CkksElement)
|
||||
InvNTT(CkksElement)
|
||||
IsNTT() bool
|
||||
SetIsNTT(bool)
|
||||
}
|
||||
246
ckks/ciphertext.go
Normal file
246
ckks/ciphertext.go
Normal file
@@ -0,0 +1,246 @@
|
||||
package ckks
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
type Ciphertext BigPoly
|
||||
|
||||
// NewCiphertext creates a new ciphertext parametised by degree, level and scale.
|
||||
func (ckkscontext *CkksContext) NewCiphertext(degree uint64, level uint64, scale uint64) *Ciphertext {
|
||||
ciphertext := new(Ciphertext)
|
||||
ciphertext.ckkscontext = ckkscontext
|
||||
ciphertext.value = make([]*ring.Poly, degree+1)
|
||||
for i := uint64(0); i < degree+1; i++ {
|
||||
ciphertext.value[i] = ckkscontext.contextLevel[level].NewPoly()
|
||||
}
|
||||
|
||||
ciphertext.scale = scale
|
||||
ciphertext.currentModulus = ring.Copy(ckkscontext.contextLevel[level].ModulusBigint)
|
||||
ciphertext.isNTT = true
|
||||
|
||||
return ciphertext
|
||||
}
|
||||
|
||||
// Value returns the ciphertext polynomial.
|
||||
func (ctx *Ciphertext) Value() []*ring.Poly {
|
||||
return ctx.value
|
||||
}
|
||||
|
||||
// SetValue assigns a polynomial to a ciphertext value.
|
||||
func (ctx *Ciphertext) SetValue(value []*ring.Poly) {
|
||||
ctx.value = value
|
||||
}
|
||||
|
||||
// Resize resize the degree of a ciphertext, by allocating or removing polynomial.
|
||||
// To be used to format a receiver of the wrong degree to a receiver of the correct degree.
|
||||
func (ctx *Ciphertext) Resize(degree uint64) {
|
||||
if ctx.Degree() > degree {
|
||||
ctx.value = ctx.value[:degree+1]
|
||||
} else if ctx.Degree() < degree {
|
||||
for ctx.Degree() < degree {
|
||||
ctx.value = append(ctx.value, []*ring.Poly{ctx.ckkscontext.contextLevel[ctx.Level()].NewPoly()}...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CkksContext returns the ckkscontext of the ciphertext.
|
||||
func (ctx *Ciphertext) CkksContext() *CkksContext {
|
||||
return ctx.ckkscontext
|
||||
}
|
||||
|
||||
// Set CkksContext assign a new ckkscontext to the ciphertext.
|
||||
func (ctx *Ciphertext) SetCkksContext(ckkscontext *CkksContext) {
|
||||
ctx.ckkscontext = ckkscontext
|
||||
}
|
||||
|
||||
// CurrentModulus returns the current modulus of the ciphertext.
|
||||
// This value is only used at the decryption process.
|
||||
func (ctx *Ciphertext) CurrentModulus() *ring.Int {
|
||||
return ctx.currentModulus
|
||||
}
|
||||
|
||||
// SetCurrentModulus assigns a new modulus to the ciphertext. This
|
||||
// value is only used at the decryption process.
|
||||
func (ctx *Ciphertext) SetCurrentModulus(modulus *ring.Int) {
|
||||
ctx.currentModulus = ring.Copy(modulus)
|
||||
}
|
||||
|
||||
// Degree returns the current degree of the ciphertext.
|
||||
func (ctx *Ciphertext) Degree() uint64 {
|
||||
return uint64(len(ctx.value) - 1)
|
||||
}
|
||||
|
||||
// Level returns the current level of the ciphertext.
|
||||
func (ctx *Ciphertext) Level() uint64 {
|
||||
return uint64(len(ctx.value[0].Coeffs) - 1)
|
||||
}
|
||||
|
||||
// Scale returns the current scale of the ciphertext.
|
||||
func (ctx *Ciphertext) Scale() uint64 {
|
||||
return ctx.scale
|
||||
}
|
||||
|
||||
// SetScale assigns a new scale to the ciphertext.
|
||||
// Assagning a new scale to a ciphertext can be used
|
||||
// to obtain free division or multiplication by 2^n.
|
||||
func (ctx *Ciphertext) SetScale(scale uint64) {
|
||||
ctx.scale = scale
|
||||
}
|
||||
|
||||
// IsNTT returns true if the ciphertext is in NTT form,
|
||||
// else returns false.
|
||||
func (ctx *Ciphertext) IsNTT() bool {
|
||||
return ctx.isNTT
|
||||
}
|
||||
|
||||
// SetIsNTT assigns a new value to the isNTT variable of the
|
||||
// ciphertext.
|
||||
func (ctx *Ciphertext) SetIsNTT(isNTT bool) {
|
||||
ctx.isNTT = isNTT
|
||||
}
|
||||
|
||||
// NTT computes the NTT of the ciphertext and copies it on the receiver element.
|
||||
// Can only be used if the reference ciphertext is not already in the NTT domain.
|
||||
func (ctx *Ciphertext) NTT(ct0 CkksElement) {
|
||||
|
||||
if ctx.isNTT != true {
|
||||
for i := range ct0.Value() {
|
||||
ctx.ckkscontext.contextLevel[ctx.Level()].NTT(ctx.value[i], ct0.Value()[i])
|
||||
}
|
||||
ct0.SetIsNTT(true)
|
||||
}
|
||||
}
|
||||
|
||||
// InvNTT computes the inverse NTT of the ciphertext and copies it on the receiver element.
|
||||
// Can only be used if the reference ciphertext is in the NTT domain.
|
||||
func (ctx *Ciphertext) InvNTT(ct0 CkksElement) {
|
||||
|
||||
if ctx.isNTT != false {
|
||||
for i := range ct0.Value() {
|
||||
ctx.ckkscontext.contextLevel[ctx.Level()].InvNTT(ctx.value[i], ct0.Value()[i])
|
||||
}
|
||||
ct0.SetIsNTT(false)
|
||||
}
|
||||
}
|
||||
|
||||
// NewRandoMCiphertext generates a new uniformely distributed ciphertext of degree, level and scale.
|
||||
func (ckkscontext *CkksContext) NewRandomCiphertext(degree, level, scale uint64) *Ciphertext {
|
||||
ciphertext := new(Ciphertext)
|
||||
ciphertext.ckkscontext = ckkscontext
|
||||
|
||||
ciphertext.value = make([]*ring.Poly, degree+1)
|
||||
for i := uint64(0); i < degree+1; i++ {
|
||||
ciphertext.value[i] = ckkscontext.contextLevel[level].NewUniformPoly()
|
||||
}
|
||||
|
||||
ciphertext.scale = scale
|
||||
ciphertext.currentModulus = ring.Copy(ckkscontext.contextLevel[level].ModulusBigint)
|
||||
ciphertext.isNTT = true
|
||||
|
||||
return ciphertext
|
||||
}
|
||||
|
||||
// CopyNew generates a new copy of the reference ciphertext, with the
|
||||
// same value and same parameters.
|
||||
func (ctx *Ciphertext) CopyNew() CkksElement {
|
||||
|
||||
ctxCopy := new(Ciphertext)
|
||||
|
||||
ctxCopy.value = make([]*ring.Poly, ctx.Degree()+1)
|
||||
for i := range ctx.value {
|
||||
ctxCopy.value[i] = ctx.value[i].CopyNew()
|
||||
}
|
||||
ctxCopy.ckkscontext = ctx.ckkscontext
|
||||
ctx.CopyParams(ctxCopy)
|
||||
|
||||
return ctxCopy
|
||||
}
|
||||
|
||||
// Copy copies the reference ciphertext and its parameters on the receiver ciphertext.
|
||||
func (ctx *Ciphertext) Copy(ctxCopy CkksElement) error {
|
||||
|
||||
if !checkContext([]CkksElement{ctx, ctxCopy}) {
|
||||
return errors.New("input ciphertext are not using the same ckkscontext")
|
||||
}
|
||||
|
||||
ctxCopy.Resize(ctx.Degree())
|
||||
|
||||
for i := range ctxCopy.Value() {
|
||||
ctx.value[i].Copy(ctxCopy.Value()[i])
|
||||
}
|
||||
|
||||
ctx.CopyParams(ctxCopy)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CopyParams copies the reference ciphertext parameters on the
|
||||
// receiver ciphertext.
|
||||
func (ctx *Ciphertext) CopyParams(ckkselement CkksElement) {
|
||||
ckkselement.SetCurrentModulus(ctx.CurrentModulus())
|
||||
ckkselement.SetScale(ctx.Scale())
|
||||
ckkselement.SetIsNTT(ctx.IsNTT())
|
||||
}
|
||||
|
||||
// MarshalBinary converts a ciphertext to an array of bytes
|
||||
func (ctx *Ciphertext) MarshalBinary() ([]byte, error) {
|
||||
var err error
|
||||
|
||||
N := uint64(len(ctx.value[0].Coeffs[0]))
|
||||
numberModuli := uint64(len(ctx.value[0].Coeffs))
|
||||
degree := uint64(len(ctx.value))
|
||||
|
||||
if numberModuli > 0xFF {
|
||||
return nil, errors.New("error : ciphertext numberModuli overflows uint8")
|
||||
}
|
||||
|
||||
if degree > 0xFF {
|
||||
return nil, errors.New("error : ciphertext degree overflows uint8")
|
||||
}
|
||||
|
||||
data := make([]byte, 3+((N*numberModuli*degree)<<3))
|
||||
|
||||
data[0] = uint8(bits.Len64(uint64(N)) - 1)
|
||||
data[1] = uint8(numberModuli)
|
||||
data[2] = uint8(degree)
|
||||
|
||||
pointer := uint64(3)
|
||||
|
||||
for i := uint64(0); i < degree; i++ {
|
||||
if pointer, err = ring.WriteCoeffsTo(pointer, N, numberModuli, ctx.value[i].Coeffs, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary converts an array of bytes to a ciphertext
|
||||
func (ctx *Ciphertext) UnmarshalBinary(data []byte) error {
|
||||
|
||||
N := uint64(1 << data[0])
|
||||
numberModuli := uint64(data[1])
|
||||
degree := uint64(data[2])
|
||||
|
||||
ctx.value = make([]*ring.Poly, degree)
|
||||
|
||||
for i := uint64(0); i < degree; i++ {
|
||||
ctx.value[i] = new(ring.Poly)
|
||||
ctx.value[i].Coeffs = make([][]uint64, numberModuli)
|
||||
}
|
||||
|
||||
pointer := uint64(3)
|
||||
|
||||
if ((uint64(len(data)) - pointer) >> 3) != N*numberModuli*degree {
|
||||
return errors.New("error : invalid PublicKey encoding")
|
||||
}
|
||||
|
||||
for x := uint64(0); x < degree; x++ {
|
||||
pointer, _ = ring.DecodeCoeffs(pointer, N, numberModuli, ctx.value[x].Coeffs, data)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
250
ckks/ckks.go
Normal file
250
ckks/ckks.go
Normal file
@@ -0,0 +1,250 @@
|
||||
package ckks
|
||||
|
||||
import (
|
||||
"github.com/lca1/lattigo/ring"
|
||||
"math"
|
||||
)
|
||||
|
||||
type CkksContext struct {
|
||||
|
||||
// Context parameters
|
||||
logN uint64
|
||||
logQ uint64
|
||||
logPrecision uint64
|
||||
logScale uint64
|
||||
n uint64
|
||||
slots uint64
|
||||
|
||||
// Uperbound in bits of the modulie
|
||||
maxBit uint64
|
||||
|
||||
// Number of avaliable levels
|
||||
levels uint64
|
||||
|
||||
// Modulie chain
|
||||
modulie []uint64
|
||||
|
||||
// Contexts chain
|
||||
contextLevel []*ring.Context
|
||||
|
||||
// Keys' context
|
||||
keyscontext *ring.Context
|
||||
|
||||
// Pre-computed values for the rescaling
|
||||
rescalParams [][]uint64
|
||||
|
||||
// Sampling variance
|
||||
sigma float64
|
||||
|
||||
// Samplers
|
||||
gaussianSampler *ring.KYSampler
|
||||
ternarySampler *ring.TernarySampler
|
||||
|
||||
// Galoi generator for the rotations, encoding and decoding params
|
||||
gen uint64
|
||||
genInv uint64
|
||||
|
||||
// Rotation params
|
||||
galElRotRow uint64
|
||||
galElRotColLeft []uint64
|
||||
galElRotColRight []uint64
|
||||
|
||||
// Encoding and Decoding params
|
||||
indexMatrix []uint64
|
||||
gap uint64
|
||||
roots []complex128
|
||||
inv_roots []complex128
|
||||
|
||||
// Checksum of [n, [modulies]]
|
||||
checksum []byte
|
||||
}
|
||||
|
||||
// NewCkksContext generates a new ckkscontext, given the parameters logN, logQ, logScale (in base 2),
|
||||
// levels and sigma.
|
||||
func NewCkksContext(logN, logQ, logScale, levels uint64, sigma float64) (*CkksContext, error) {
|
||||
|
||||
var err error
|
||||
|
||||
ckkscontext := new(CkksContext)
|
||||
|
||||
ckkscontext.logN = logN
|
||||
ckkscontext.n = 1 << logN
|
||||
ckkscontext.slots = 1 << (logN - 1)
|
||||
ckkscontext.logQ = logQ
|
||||
ckkscontext.logScale = logScale
|
||||
ckkscontext.levels = levels
|
||||
|
||||
ckkscontext.maxBit = 60 // The first prime is always 60 bits to ensure some bits of precision for integers.
|
||||
|
||||
// ========== START < PRIMES GENERATION > START ===============
|
||||
// Search for suitable primes
|
||||
if ckkscontext.modulie, ckkscontext.logPrecision, err = GenerateCKKSPrimes(logQ, logN, levels); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If logQ is smaller than maxBit, then computes the first prime of size maxbit.
|
||||
var tmp []uint64
|
||||
if logQ < ckkscontext.maxBit {
|
||||
tmp, _, _ = GenerateCKKSPrimes(ckkscontext.maxBit, logN, 1)
|
||||
|
||||
ckkscontext.modulie[0] = tmp[0]
|
||||
}
|
||||
// ========== END < PRIMES GENERATION > END ===============
|
||||
|
||||
// ========== START < CONTEXTS CHAIN > START ===============
|
||||
ckkscontext.contextLevel = make([]*ring.Context, levels)
|
||||
|
||||
ckkscontext.contextLevel[0] = ring.NewContext()
|
||||
|
||||
if err = ckkscontext.contextLevel[0].SetParameters(1<<logN, ckkscontext.modulie[:1]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = ckkscontext.contextLevel[0].ValidateParameters(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := uint64(1); i < levels; i++ {
|
||||
|
||||
ckkscontext.contextLevel[i] = ring.NewContext()
|
||||
|
||||
if err = ckkscontext.contextLevel[i].SetParameters(1<<logN, ckkscontext.modulie[i:i+1]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = ckkscontext.contextLevel[i].ValidateParameters(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Instead of recomputing and storing redundant context, subsequent contexts are a merge of previous contexts.
|
||||
if err = ckkscontext.contextLevel[i].Merge(ckkscontext.contextLevel[i-1], ckkscontext.contextLevel[i]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// ========== END < CONTEXTS CHAIN > END ===============
|
||||
|
||||
// Context used for the generation of the keys
|
||||
ckkscontext.keyscontext = ckkscontext.contextLevel[levels-1]
|
||||
|
||||
// ========== START < RESCALE PRE-COMPUATION PARAMETERS > START ===============
|
||||
var Qi, Ql uint64
|
||||
|
||||
ckkscontext.rescalParams = make([][]uint64, levels-1)
|
||||
|
||||
for j := uint64(levels) - 1; j > 0; j-- {
|
||||
|
||||
ckkscontext.rescalParams[j-1] = make([]uint64, j)
|
||||
|
||||
Ql = ckkscontext.modulie[j]
|
||||
|
||||
bredParams := ckkscontext.contextLevel[j-1].GetBredParams()
|
||||
|
||||
for i := uint64(0); i < j; i++ {
|
||||
|
||||
Qi = ckkscontext.modulie[i]
|
||||
|
||||
ckkscontext.rescalParams[j-1][i] = ring.MForm(modexp(Ql, Qi-2, Qi), Qi, bredParams[i])
|
||||
}
|
||||
}
|
||||
// ========== END < RESCALE PRE-COMPUATION PARAMETERS > END ===============
|
||||
|
||||
// default variance
|
||||
ckkscontext.sigma = sigma
|
||||
|
||||
ckkscontext.gaussianSampler = ckkscontext.keyscontext.NewKYSampler(sigma, int(6*sigma))
|
||||
ckkscontext.ternarySampler = ckkscontext.keyscontext.NewTernarySampler()
|
||||
|
||||
// ========== START < ROTATION ELEMENTS > START ===============
|
||||
var m, mask uint64
|
||||
|
||||
m = ckkscontext.n << 1
|
||||
|
||||
mask = m - 1
|
||||
|
||||
ckkscontext.gen = 5 // Any integer equal to 1 mod 4 and comprime to 2N will do fine
|
||||
ckkscontext.genInv = modexp(ckkscontext.gen, mask, m)
|
||||
|
||||
ckkscontext.galElRotColLeft = make([]uint64, ckkscontext.slots)
|
||||
ckkscontext.galElRotColRight = make([]uint64, ckkscontext.slots)
|
||||
|
||||
ckkscontext.galElRotColRight[0] = 1
|
||||
ckkscontext.galElRotColLeft[0] = 1
|
||||
|
||||
for i := uint64(1); i < ckkscontext.slots; i++ {
|
||||
ckkscontext.galElRotColLeft[i] = (ckkscontext.galElRotColLeft[i-1] * ckkscontext.gen) & mask
|
||||
ckkscontext.galElRotColRight[i] = (ckkscontext.galElRotColRight[i-1] * ckkscontext.genInv) & mask
|
||||
}
|
||||
|
||||
ckkscontext.galElRotRow = mask
|
||||
// ============ END < ROTATION ELEMENTS > END ================
|
||||
|
||||
// ========== START < ENCODER PARAMETERS > START ================
|
||||
var pos, index1, index2 uint64
|
||||
|
||||
ckkscontext.gap = 1 //gap-1 is the gap between each slot, here 1 means no gap.
|
||||
|
||||
ckkscontext.indexMatrix = make([]uint64, ckkscontext.n)
|
||||
|
||||
pos = 1
|
||||
|
||||
for i := uint64(0); i < ckkscontext.slots; i++ {
|
||||
|
||||
index1 = (pos - 1) >> 1
|
||||
index2 = (m - pos - 1) >> 1
|
||||
|
||||
ckkscontext.indexMatrix[i] = bitReverse64(index1, ckkscontext.logN)
|
||||
ckkscontext.indexMatrix[i|ckkscontext.slots] = bitReverse64(index2, ckkscontext.logN)
|
||||
|
||||
pos *= ckkscontext.gen
|
||||
pos &= mask
|
||||
}
|
||||
|
||||
ckkscontext.roots = make([]complex128, ckkscontext.n)
|
||||
ckkscontext.inv_roots = make([]complex128, ckkscontext.n)
|
||||
|
||||
angle := 6.283185307179586 / float64(m)
|
||||
psi := complex(math.Cos(angle), math.Sin(angle))
|
||||
psiInv := complex(1, 0) / psi
|
||||
|
||||
ckkscontext.roots[0] = 1
|
||||
ckkscontext.inv_roots[0] = 1
|
||||
|
||||
for j := uint64(1); j < ckkscontext.n; j++ {
|
||||
|
||||
indexReversePrev := bitReverse64(j-1, ckkscontext.logN)
|
||||
indexReverseNext := bitReverse64(j, ckkscontext.logN)
|
||||
|
||||
ckkscontext.roots[indexReverseNext] = ckkscontext.roots[indexReversePrev] * psi
|
||||
ckkscontext.inv_roots[indexReverseNext] = ckkscontext.inv_roots[indexReversePrev] * psiInv
|
||||
}
|
||||
// ========== END < ENCODER PARAMETERS > END ================
|
||||
|
||||
// =========== START < CHECKSUM > =============
|
||||
toHash := make([]uint64, 1+levels)
|
||||
toHash[0] = logN
|
||||
for i := 1; i < len(toHash); i++ {
|
||||
toHash[i] = ckkscontext.modulie[i-1]
|
||||
}
|
||||
|
||||
if ckkscontext.checksum, err = Hash(toHash); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// ========== END < CHECKSUM > END
|
||||
|
||||
return ckkscontext, nil
|
||||
|
||||
}
|
||||
|
||||
func (ckkscontext *CkksContext) Precision() uint64 {
|
||||
return ckkscontext.logPrecision
|
||||
}
|
||||
|
||||
func (ckkscontext *CkksContext) ContextKeys() *ring.Context {
|
||||
return ckkscontext.keyscontext
|
||||
}
|
||||
|
||||
func (ckkscontext *CkksContext) Slots() uint64 {
|
||||
return (1 << (ckkscontext.logN - 1))
|
||||
}
|
||||
|
||||
// TODO Get parameters
|
||||
247
ckks/ckks_benchmarks_test.go
Executable file
247
ckks/ckks_benchmarks_test.go
Executable file
@@ -0,0 +1,247 @@
|
||||
package ckks
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type benchParams struct {
|
||||
logN uint64
|
||||
logQ uint64
|
||||
levels uint64
|
||||
logScale uint64
|
||||
sigma float64
|
||||
bdc uint64
|
||||
}
|
||||
|
||||
func BenchmarkCKKSScheme(b *testing.B) {
|
||||
|
||||
var err error
|
||||
var ckkscontext *CkksContext
|
||||
var kgen *KeyGenerator
|
||||
var sk *SecretKey
|
||||
var pk *PublicKey
|
||||
var rlk *EvaluationKey
|
||||
var rotkey *RotationKey
|
||||
var encryptor *Encryptor
|
||||
var decryptor *Decryptor
|
||||
var evaluator *Evaluator
|
||||
var plaintext *Plaintext
|
||||
var ciphertext *Ciphertext
|
||||
|
||||
params := []benchParams{
|
||||
//{logN: 10, logQ: 30, levels: 1, logScale : 20, sigma: 3.19, bdc : 60},
|
||||
//{logN: 11, logQ: 60, levels: 1, logScale : 40, sigma: 3.19, bdc : 60},
|
||||
{logN: 12, logQ: 40, levels: 2, logScale: 40, sigma: 3.2, bdc: 60},
|
||||
{logN: 13, logQ: 40, levels: 4, logScale: 40, sigma: 3.2, bdc: 60},
|
||||
{logN: 14, logQ: 40, levels: 8, logScale: 40, sigma: 3.2, bdc: 60},
|
||||
{logN: 15, logQ: 40, levels: 16, logScale: 40, sigma: 3.2, bdc: 60},
|
||||
}
|
||||
|
||||
var logN, logQ, logScale, levels, bdc uint64
|
||||
var sigma float64
|
||||
|
||||
for _, param := range params {
|
||||
|
||||
logN = param.logN
|
||||
logQ = param.logQ
|
||||
logScale = param.logScale
|
||||
levels = param.levels
|
||||
sigma = param.sigma
|
||||
bdc = param.bdc
|
||||
|
||||
if ckkscontext, err = NewCkksContext(logN, logQ, logScale, levels, sigma); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
kgen = ckkscontext.NewKeyGenerator()
|
||||
|
||||
if sk, pk, err = kgen.NewKeyPair(); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
if rlk, err = kgen.NewRelinKey(sk, bdc); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
if rotkey, err = kgen.NewRotationKeysPow2(sk, bdc, true); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
if encryptor, err = ckkscontext.NewEncryptor(pk); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
if decryptor, err = ckkscontext.NewDecryptor(sk); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
evaluator = ckkscontext.NewEvaluator()
|
||||
|
||||
_ = rotkey
|
||||
|
||||
ciphertext = ckkscontext.NewCiphertext(1, levels-1, logScale)
|
||||
|
||||
var values []complex128
|
||||
b.Run(fmt.Sprintf("logN=%d/logQ=%d/Levels=%d/decomp=%d/sigma=%.2f/Encode", logN, logQ, levels, bdc, sigma), func(b *testing.B) {
|
||||
slots := 1 << (logN - 2)
|
||||
values = make([]complex128, slots)
|
||||
for i := 0; i < slots; i++ {
|
||||
values[i] = complex(randomFloat(0.1, 1), 0)
|
||||
}
|
||||
|
||||
plaintext = ckkscontext.NewPlaintext(levels-1, logScale)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err = plaintext.EncodeComplex(values); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
b.Run(fmt.Sprintf("logN=%d/logQ=%d/levels=%d/decomp=%d/sigma=%.2f/Decode", logN, logQ, levels, bdc, sigma), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if values = plaintext.DecodeComplex(); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Key Pair Generation
|
||||
b.Run(fmt.Sprintf("logN=%d/logQ=%d/levels=%d/decomp=%d/sigma=%.2f/KeyPairGen", logN, logQ, levels, bdc, sigma), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sk, pk, err = kgen.NewKeyPair()
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// SwitchKeyGen
|
||||
b.Run(fmt.Sprintf("logN=%d/logQ=%d/levels=%d/decomp=%d/sigma=%.2f/SwitchKeyGen", logN, logQ, levels, bdc, sigma), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if rlk, err = kgen.NewRelinKey(sk, bdc); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Encrypt
|
||||
b.Run(fmt.Sprintf("logN=%d/logQ=%d/levels=%d/decomp=%d/sigma=%.2f/Encrypt", logN, logQ, levels, bdc, sigma), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err = encryptor.Encrypt(plaintext, ciphertext); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Decrypt
|
||||
b.Run(fmt.Sprintf("logN=%d/logQ=%d/levels=%d/decomp=%d/sigma=%.2f/Decrypt", logN, logQ, levels, bdc, sigma), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err = decryptor.Decrypt(ciphertext, plaintext); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Add
|
||||
b.Run(fmt.Sprintf("logN=%d/logQ=%d/levels=%d/decomp=%d/sigma=%.2f/Add", logN, logQ, levels, bdc, sigma), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err = evaluator.Add(ciphertext, ciphertext, ciphertext); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Add Scalar
|
||||
b.Run(fmt.Sprintf("logN=%d/logQ=%d/levels=%d/decomp=%d/sigma=%.2f/AddScalar", logN, logQ, levels, bdc, sigma), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err = evaluator.AddConst(ciphertext, complex(3.1415, -1.4142), ciphertext); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Mult Scalar
|
||||
b.Run(fmt.Sprintf("logN=%d/logQ=%d/levels=%d/decomp=%d/sigma=%.2f/MultScalar", logN, logQ, levels, bdc, sigma), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err = evaluator.MultConst(ciphertext, complex(3.1415, -1.4142), ciphertext); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Mul
|
||||
receiver := ckkscontext.NewRandomCiphertext(2, levels-1, logScale)
|
||||
b.Run(fmt.Sprintf("logN=%d/logQ=%d/levels=%d/decomp=%d/sigma=%.2f/Multiply", logN, logQ, levels, bdc, sigma), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err = evaluator.MulRelin(ciphertext, ciphertext, nil, receiver); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Mul Relin
|
||||
b.Run(fmt.Sprintf("logN=%d/logQ=%d/levels=%d/decomp=%d/sigma=%.2f/MulRelin", logN, logQ, levels, bdc, sigma), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err = evaluator.MulRelin(ciphertext, ciphertext, rlk, ciphertext); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Square
|
||||
b.Run(fmt.Sprintf("logN=%d/logQ=%d/levels=%d/decomp=%d/sigma=%.2f/Square", logN, logQ, levels, bdc, sigma), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err = evaluator.Square(ciphertext, nil, receiver); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Square Relin
|
||||
b.Run(fmt.Sprintf("logN=%d/logQ=%d/levels=%d/decomp=%d/sigma=%.2f/SquareRelin", logN, logQ, levels, bdc, sigma), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err = evaluator.Square(ciphertext, rlk, ciphertext); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Relin
|
||||
|
||||
b.Run(fmt.Sprintf("logN=%d/logQ=%d/levels=%d/decomp=%d/sigma=%.2f/Relin", logN, logQ, levels, bdc, sigma), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err = evaluator.Relinearize(receiver, rlk, ciphertext); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Rescale
|
||||
b.Run(fmt.Sprintf("logN=%d/logQ=%d/levels=%d/decomp=%d/sigma=%.2f/Rescale", logN, logQ, levels, bdc, sigma), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
rescale(evaluator, ciphertext.Value()[0], receiver.Value()[0])
|
||||
rescale(evaluator, ciphertext.Value()[1], receiver.Value()[1])
|
||||
}
|
||||
})
|
||||
|
||||
// Conjugate / Rotate
|
||||
b.Run(fmt.Sprintf("logN=%d/logQ=%d/levels=%d/decomp=%d/sigma=%.2f/Conjugate", logN, logQ, levels, bdc, sigma), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err = evaluator.Conjugate(ciphertext, rotkey, ciphertext); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Rotate Cols
|
||||
b.Run(fmt.Sprintf("logN=%d/logQ=%d/levels=%d/decomp=%d/sigma=%.2f/RotateCols", logN, logQ, levels, bdc, sigma), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err = evaluator.RotateColumns(ciphertext, 1, rotkey, ciphertext); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
1624
ckks/ckks_test.go
Normal file
1624
ckks/ckks_test.go
Normal file
File diff suppressed because it is too large
Load Diff
69
ckks/decryptor.go
Normal file
69
ckks/decryptor.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package ckks
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
type Decryptor struct {
|
||||
ckkscontext *CkksContext
|
||||
sk *SecretKey
|
||||
}
|
||||
|
||||
// NewDecryptor instanciates a new decryptor that will be able to decrypt ciphertext
|
||||
// encrypted under the provided key.
|
||||
func (ckkscontext *CkksContext) NewDecryptor(sk *SecretKey) (*Decryptor, error) {
|
||||
|
||||
if sk.sk.GetDegree() != int(ckkscontext.n) {
|
||||
return nil, errors.New("error : secret_key degree must match context degree")
|
||||
}
|
||||
|
||||
decryptor := new(Decryptor)
|
||||
|
||||
decryptor.ckkscontext = ckkscontext
|
||||
|
||||
decryptor.sk = sk
|
||||
|
||||
return decryptor, nil
|
||||
}
|
||||
|
||||
// DecryptNew decrypts the ciphertext and returns a newly created plaintext.
|
||||
func (decryptor *Decryptor) DecryptNew(ciphertext *Ciphertext) (plaintext *Plaintext, err error) {
|
||||
|
||||
plaintext = decryptor.ckkscontext.NewPlaintext(ciphertext.Level(), ciphertext.Scale())
|
||||
|
||||
if err = decryptor.Decrypt(ciphertext, plaintext); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
// Decrypt decrypts the ciphertext and returns the result on the provided receiver plaintext.
|
||||
// A Horner methode is used for evaluating the decryption.
|
||||
func (decryptor *Decryptor) Decrypt(ciphertext *Ciphertext, plaintext *Plaintext) (err error) {
|
||||
|
||||
level := ciphertext.Level()
|
||||
|
||||
plaintext.SetScale(ciphertext.Scale())
|
||||
plaintext.currentModulus.SetBigInt(ciphertext.currentModulus)
|
||||
|
||||
if err = ciphertext.value[ciphertext.Degree()].Copy(plaintext.value[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := uint64(ciphertext.Degree()); i > 0; i-- {
|
||||
|
||||
decryptor.ckkscontext.contextLevel[level].MulCoeffsMontgomery(plaintext.value[0], decryptor.sk.sk, plaintext.value[0])
|
||||
decryptor.ckkscontext.contextLevel[level].Add(plaintext.value[0], ciphertext.value[i-1], plaintext.value[0])
|
||||
|
||||
if i&7 == 7 {
|
||||
decryptor.ckkscontext.contextLevel[level].Reduce(plaintext.value[0], plaintext.value[0])
|
||||
}
|
||||
}
|
||||
|
||||
if (ciphertext.Degree())&7 != 7 {
|
||||
decryptor.ckkscontext.contextLevel[level].Reduce(plaintext.value[0], plaintext.value[0])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
84
ckks/encryptor.go
Normal file
84
ckks/encryptor.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package ckks
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
//"fmt"
|
||||
)
|
||||
|
||||
type Encryptor struct {
|
||||
ckkscontext *CkksContext
|
||||
pk *PublicKey // secret key
|
||||
polypool *ring.Poly
|
||||
}
|
||||
|
||||
// NewEncryptor instanciates a new Encryptor with the provided public key.
|
||||
// This encryptor can be used to encrypt plaintexts, using its the stored public key.
|
||||
func (ckkscontext *CkksContext) NewEncryptor(pk *PublicKey) (*Encryptor, error) {
|
||||
if uint64(pk.pk[0].GetDegree()+pk.pk[1].GetDegree())>>1 != ckkscontext.n {
|
||||
return nil, errors.New("error : pk ring degree doesn't match ckkscontext ring degree")
|
||||
}
|
||||
//TODO : check that pk degree matches ckksContext degree
|
||||
encryptor := new(Encryptor)
|
||||
encryptor.ckkscontext = ckkscontext
|
||||
encryptor.pk = pk
|
||||
encryptor.polypool = ckkscontext.contextLevel[ckkscontext.levels-1].NewPoly()
|
||||
return encryptor, nil
|
||||
}
|
||||
|
||||
// EncryptNew encrypts the provided plaintext under the stored public key and returns
|
||||
// the ciphertext on a newly created element.
|
||||
func (encryptor *Encryptor) EncryptNew(plaintext *Plaintext) (*Ciphertext, error) {
|
||||
|
||||
if uint64(plaintext.value[0].GetDegree()) != encryptor.ckkscontext.n {
|
||||
return nil, errors.New("error : plaintext ring degree doesn't match encryptor ckkscontext ring degree")
|
||||
}
|
||||
|
||||
ciphertext := encryptor.ckkscontext.NewCiphertext(1, plaintext.Level(), plaintext.Scale())
|
||||
|
||||
encrypt(encryptor, plaintext, ciphertext)
|
||||
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
// Encrypt encrypts the provided plaitnext under the stored public key, and returns the result
|
||||
// on the provided reciver ciphertext.
|
||||
func (encryptor *Encryptor) Encrypt(plaintext *Plaintext, ciphertext *Ciphertext) error {
|
||||
|
||||
if uint64(plaintext.value[0].GetDegree()) != encryptor.ckkscontext.n {
|
||||
return errors.New("error : plaintext ring degree doesn't match encryptor ckkscontext ring degree")
|
||||
}
|
||||
|
||||
if ciphertext.Degree() != 1 {
|
||||
return errors.New("error : invalide receiver -> ciphertext degree > 1")
|
||||
}
|
||||
|
||||
plaintext.CopyParams(ciphertext)
|
||||
|
||||
encrypt(encryptor, plaintext, ciphertext)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func encrypt(encryptor *Encryptor, plaintext *Plaintext, ciphertext *Ciphertext) {
|
||||
|
||||
context := encryptor.ckkscontext.contextLevel[plaintext.Level()]
|
||||
|
||||
encryptor.ckkscontext.ternarySampler.SampleMontgomeryNTT(encryptor.polypool)
|
||||
|
||||
context.MulCoeffsMontgomery(encryptor.polypool, encryptor.pk.pk[0], ciphertext.value[0])
|
||||
context.MulCoeffsMontgomery(encryptor.polypool, encryptor.pk.pk[1], ciphertext.value[1])
|
||||
|
||||
context.Add(ciphertext.value[0], plaintext.value[0], ciphertext.value[0])
|
||||
|
||||
encryptor.ckkscontext.gaussianSampler.SampleNTT(encryptor.polypool)
|
||||
context.Add(ciphertext.value[0], encryptor.polypool, ciphertext.value[0])
|
||||
|
||||
encryptor.ckkscontext.gaussianSampler.SampleNTT(encryptor.polypool)
|
||||
context.Add(ciphertext.value[1], encryptor.polypool, ciphertext.value[1])
|
||||
|
||||
encryptor.polypool.Zero()
|
||||
|
||||
ciphertext.isNTT = true
|
||||
|
||||
}
|
||||
1695
ckks/evaluator.go
Normal file
1695
ckks/evaluator.go
Normal file
File diff suppressed because it is too large
Load Diff
227
ckks/function_approximations.go
Normal file
227
ckks/function_approximations.go
Normal file
@@ -0,0 +1,227 @@
|
||||
package ckks
|
||||
|
||||
import (
|
||||
//"fmt"
|
||||
"math"
|
||||
"math/cmplx"
|
||||
)
|
||||
|
||||
func chebyshevNodesU(n int, a, b complex128) (u []complex128) {
|
||||
u = make([]complex128, n)
|
||||
var x, y complex128
|
||||
for k := 1; k < n+1; k++ {
|
||||
x = 0.5 * (a + b)
|
||||
y = 0.5 * (b - a)
|
||||
u[n-k] = x + y*complex(math.Cos((float64(k)-0.5)*(3.141592653589793/float64(n))), 0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func chebyshevNodesX(u []complex128, a, b complex128) (x []complex128) {
|
||||
x = make([]complex128, len(u))
|
||||
for i := 0; i < len(u); i++ {
|
||||
x[i] = 0.5*(b-a)*u[i] + 0.5*(a+b)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func average(y []complex128) (avg complex128) {
|
||||
avg = 0
|
||||
for i := 0; i < len(y); i++ {
|
||||
avg += y[i]
|
||||
}
|
||||
avg /= complex(float64(len(y)), 0)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func evaluate_cheby(coeffs []complex128, x complex128, a, b complex128) (y complex128) {
|
||||
var u, Tprev, Tnext, T complex128
|
||||
u = (2*x - a - b) / (b - a)
|
||||
Tprev = 1
|
||||
T = u
|
||||
y = coeffs[0]
|
||||
for i := 1; i < len(coeffs); i++ {
|
||||
y = y + T*coeffs[i]
|
||||
Tnext = 2*u*T - Tprev
|
||||
Tprev = T
|
||||
T = Tnext
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evaluate(degree int, x, a, b complex128) (T complex128) {
|
||||
if degree == 0 {
|
||||
return 1
|
||||
}
|
||||
var u, Tprev, Tnext complex128
|
||||
u = (2*x - a - b) / (b - a)
|
||||
Tprev = 1
|
||||
T = u
|
||||
for i := 1; i < degree; i++ {
|
||||
Tnext = 2*u*T - Tprev
|
||||
Tprev = T
|
||||
T = Tnext
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func chebyCoeffs(u, y []complex128, a, b complex128) (coeffs []complex128) {
|
||||
n := len(y)
|
||||
coeffs = make([]complex128, n)
|
||||
var tmp []complex128
|
||||
for i := 0; i < n; i++ {
|
||||
tmp = make([]complex128, n)
|
||||
for j := 0; j < n; j++ {
|
||||
tmp[j] = y[j] * evaluate(i, u[j], -1, 1)
|
||||
}
|
||||
if i != 0 {
|
||||
coeffs[i] = 2 * average(tmp)
|
||||
} else {
|
||||
coeffs[i] = average(tmp)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type ChebyshevInterpolation struct {
|
||||
coeffs []complex128
|
||||
a complex128
|
||||
b complex128
|
||||
}
|
||||
|
||||
// Approximate computes a Chebyshev approximation of the input function, for the tange [-a, b] of degree degree.
|
||||
// To be used in conjonction with the function EvaluateCheby.
|
||||
func Approximate(function func(complex128) complex128, a, b complex128, degree int) (cheby *ChebyshevInterpolation) {
|
||||
|
||||
cheby = new(ChebyshevInterpolation)
|
||||
cheby.coeffs = make([]complex128, degree+1)
|
||||
cheby.a = a
|
||||
cheby.b = b
|
||||
|
||||
u := chebyshevNodesU(degree, -1, 1)
|
||||
x := chebyshevNodesX(u, a, b)
|
||||
|
||||
y := make([]complex128, len(x))
|
||||
for i := range x {
|
||||
y[i] = function(x[i])
|
||||
}
|
||||
|
||||
cheby.coeffs = chebyCoeffs(u, y, a, b)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Given a hash table with the first three evaluations of the Chebyshev ring at x in the interval a, b:
|
||||
// C0 = 1 (actually not stored in the hash table)
|
||||
// C1 = (2*x - a - b)/(b-a)
|
||||
// C2 = 2*C1*C1 - C0
|
||||
// Evaluates the nth degree Chebyshev ring in a recursive manner, storing intermediate results in the hashtable.
|
||||
// Consumes at most ceil(sqrt(n)) levels for an evaluation at Cn.
|
||||
// Uses the following property : for a given Chebyshev ring Cn = 2*Ca*Cb - Cc, n = a+b and c = abs(a-b)
|
||||
func evaluateCheby(n uint64, C map[uint64]CkksElement, evaluator *Evaluator, evakey *EvaluationKey) (err error) {
|
||||
|
||||
if C[n] == nil {
|
||||
|
||||
// Computes the index required to compute the asked ring evaluation
|
||||
a := uint64(math.Ceil(float64(n) / 2))
|
||||
b := n >> 1
|
||||
c := uint64(math.Abs(float64(a) - float64(b)))
|
||||
|
||||
// Recurses on the given indexes
|
||||
evaluateCheby(a, C, evaluator, evakey)
|
||||
evaluateCheby(b, C, evaluator, evakey)
|
||||
|
||||
// Since C[0] is not stored (but rather seen as the constant 1), only recurses on c if c!= 0
|
||||
if c != 0 {
|
||||
evaluateCheby(c, C, evaluator, evakey)
|
||||
}
|
||||
|
||||
// Computes C[n] = C[a]*C[b]
|
||||
if C[n], err = evaluator.MulRelinNew(C[a], C[b], evakey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
evaluator.Rescale(C[n], C[n])
|
||||
|
||||
// Computes C[n] = 2*C[a]*C[b]
|
||||
evaluator.Add(C[n], C[n], C[n])
|
||||
|
||||
// Computes C[n] = 2*C[a]*C[b] - C[c]
|
||||
if c == 0 {
|
||||
evaluator.AddConst(C[n], -1, C[n])
|
||||
} else {
|
||||
if err = evaluator.Sub(C[n], C[c], C[n]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// EvaluateCheby evaluates a chebyshev approximation in log(n) + 1 (+1 if 2/(b-a) is not a gaussian integer)
|
||||
func (evaluator *Evaluator) EvaluateCheby(ct *Ciphertext, cheby *ChebyshevInterpolation, evakey *EvaluationKey) (res *Ciphertext, err error) {
|
||||
|
||||
a := cheby.a
|
||||
b := cheby.b
|
||||
ChebyCoeffs := cheby.coeffs
|
||||
|
||||
C := make(map[uint64]CkksElement)
|
||||
|
||||
// C0 = 1, so we treat it as a constant
|
||||
// Computes C1 and C2 which are required for the rest of the recursive computation of the Chebyshev ring
|
||||
|
||||
C[1] = ct.CopyNew()
|
||||
evaluator.MultConst(C[1], 2/(b-a), C[1])
|
||||
evaluator.AddConst(C[1], (-a-b)/(b-a), C[1])
|
||||
|
||||
if C[1].Scale() > ct.Scale() {
|
||||
evaluator.Rescale(C[1], C[1])
|
||||
}
|
||||
|
||||
C[2], _ = evaluator.MulRelinNew(C[1], C[1], evakey)
|
||||
|
||||
evaluator.Rescale(C[2], C[2])
|
||||
evaluator.Add(C[2], C[2], C[2])
|
||||
evaluator.AddConst(C[2], -1, C[2])
|
||||
|
||||
res = C[1].CopyNew().(*Ciphertext) // res = C[1]
|
||||
|
||||
if err = evaluator.MultConst(res, ChebyCoeffs[1], res); err != nil { // res = A[1]*C[1]
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = evaluator.AddConst(res, ChebyCoeffs[0], res); err != nil { // res = A[0] + A[1]*C[1]
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := uint64(2); i < uint64(len(ChebyCoeffs)); i++ {
|
||||
|
||||
// Evaluates the C[i] Chebyshev ring
|
||||
if err = evaluateCheby(i, C, evaluator, evakey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = evaluator.MultByConstAndAdd(C[i], ChebyCoeffs[i], res); err != nil { // res = A[0] + A[1]*C[1] + ... + A[i]*C[i]
|
||||
return nil, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
evaluator.Rescale(res, res) // We only rescale at the end to save computation
|
||||
|
||||
return res, nil
|
||||
|
||||
}
|
||||
|
||||
func exp2pi(x complex128) complex128 {
|
||||
return cmplx.Exp(2 * 3.141592653589793 * complex(0, 1) * x)
|
||||
}
|
||||
|
||||
func sin2pi2pi(x complex128) complex128 {
|
||||
return cmplx.Sin(2*3.141592653589793*x) / (2 * 3.141592653589793)
|
||||
}
|
||||
303
ckks/keygen.go
Normal file
303
ckks/keygen.go
Normal file
@@ -0,0 +1,303 @@
|
||||
package ckks
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
"math"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
type SecretKey struct {
|
||||
sk *ring.Poly
|
||||
}
|
||||
|
||||
type PublicKey struct {
|
||||
pk [2]*ring.Poly
|
||||
}
|
||||
|
||||
type KeyGenerator struct {
|
||||
ckkscontext *CkksContext
|
||||
context *ring.Context
|
||||
polypool *ring.Poly
|
||||
}
|
||||
|
||||
type RotationKey struct {
|
||||
ckkscontext *CkksContext
|
||||
bitDecomp uint64
|
||||
evakey_rot_col_L map[uint64]*SwitchingKey
|
||||
evakey_rot_col_R map[uint64]*SwitchingKey
|
||||
evakey_rot_row *SwitchingKey
|
||||
}
|
||||
|
||||
type EvaluationKey struct {
|
||||
evakey *SwitchingKey
|
||||
}
|
||||
|
||||
type SwitchingKey struct {
|
||||
bitDecomp uint64
|
||||
evakey [][][2]*ring.Poly
|
||||
}
|
||||
|
||||
// NewKeyGenerator instantiates a new keygenerator, from which the secret and public keys, as well as the evaluation,
|
||||
// rotation and switching keys can be generated.
|
||||
func (ckkscontext *CkksContext) NewKeyGenerator() (keygen *KeyGenerator) {
|
||||
keygen = new(KeyGenerator)
|
||||
keygen.ckkscontext = ckkscontext
|
||||
keygen.context = ckkscontext.keyscontext
|
||||
keygen.polypool = ckkscontext.keyscontext.NewPoly()
|
||||
return
|
||||
}
|
||||
|
||||
func (keygen *KeyGenerator) check_sk(sk_output *SecretKey) error {
|
||||
|
||||
if sk_output.Get().GetDegree() != int(keygen.context.N) {
|
||||
return errors.New("error : pol degree sk != ckkscontext.n")
|
||||
}
|
||||
|
||||
if len(sk_output.Get().Coeffs) != len(keygen.context.Modulus) {
|
||||
return errors.New("error : nb modulus sk != nb modulus ckkscontext")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewSecretKey generates a new secret key.
|
||||
func (keygen *KeyGenerator) NewSecretKey() *SecretKey {
|
||||
sk := new(SecretKey)
|
||||
sk.sk = keygen.ckkscontext.ternarySampler.SampleMontgomeryNTTNew()
|
||||
return sk
|
||||
}
|
||||
|
||||
// Get returns the secret key value of the secret key.
|
||||
func (sk *SecretKey) Get() *ring.Poly {
|
||||
return sk.sk
|
||||
}
|
||||
|
||||
// Set sets the value of the secret key to the provided value.
|
||||
func (sk *SecretKey) Set(poly *ring.Poly) {
|
||||
sk.sk = poly.CopyNew()
|
||||
}
|
||||
|
||||
// NewPublicKey generates a new public key from the provided secret key.
|
||||
func (keygen *KeyGenerator) NewPublicKey(sk *SecretKey) (pk *PublicKey, err error) {
|
||||
|
||||
if err = keygen.check_sk(sk); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pk = new(PublicKey)
|
||||
|
||||
//pk[0] = [-(a*s + e)]
|
||||
//pk[1] = [a]
|
||||
pk.pk[0] = keygen.ckkscontext.gaussianSampler.SampleNTTNew()
|
||||
pk.pk[1] = keygen.context.NewUniformPoly()
|
||||
|
||||
keygen.context.MulCoeffsMontgomeryAndAdd(sk.sk, pk.pk[1], pk.pk[0])
|
||||
keygen.context.Neg(pk.pk[0], pk.pk[0])
|
||||
|
||||
return pk, nil
|
||||
}
|
||||
|
||||
// Get returns the value of the the public key.
|
||||
func (pk *PublicKey) Get() [2]*ring.Poly {
|
||||
return pk.pk
|
||||
}
|
||||
|
||||
// Set sets the value of the public key to the provided value.
|
||||
func (pk *PublicKey) Set(poly [2]*ring.Poly) {
|
||||
pk.pk[0] = poly[0].CopyNew()
|
||||
pk.pk[1] = poly[1].CopyNew()
|
||||
}
|
||||
|
||||
// NewKeyPair generates a new secretkey and a corresponding public key.
|
||||
func (keygen *KeyGenerator) NewKeyPair() (sk *SecretKey, pk *PublicKey, err error) {
|
||||
sk = keygen.NewSecretKey()
|
||||
pk, err = keygen.NewPublicKey(sk)
|
||||
return
|
||||
}
|
||||
|
||||
// NewRelinkey generates a new evaluation key that will be used to relinearize the ciphertexts during multiplication.
|
||||
// Bitdecomposition aims at reducing the added noise at the expense of more storage needed for the keys and more computation
|
||||
// during the relinearization. However for relinearization this bitdecomp value can be set to maximum as the encrypted value
|
||||
// are also scaled up during the multiplication.
|
||||
func (keygen *KeyGenerator) NewRelinKey(sk *SecretKey, bitDecomp uint64) (evakey *EvaluationKey, err error) {
|
||||
|
||||
if err = keygen.check_sk(sk); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
evakey = new(EvaluationKey)
|
||||
sk.Get().Copy(keygen.polypool)
|
||||
keygen.context.MulCoeffsMontgomery(keygen.polypool, sk.Get(), keygen.polypool)
|
||||
evakey.evakey = newswitchingkey(keygen.ckkscontext, keygen.polypool, sk.Get(), bitDecomp)
|
||||
keygen.polypool.Zero()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (keygen *KeyGenerator) SetRelinKeys(rlk [][][2]*ring.Poly, bitDecomp uint64) (*EvaluationKey, error) {
|
||||
|
||||
newevakey := new(EvaluationKey)
|
||||
|
||||
newevakey.evakey = new(SwitchingKey)
|
||||
newevakey.evakey.bitDecomp = bitDecomp
|
||||
newevakey.evakey.evakey = make([][][2]*ring.Poly, len(rlk))
|
||||
for j := range rlk {
|
||||
newevakey.evakey.evakey[j] = make([][2]*ring.Poly, len(rlk[j]))
|
||||
for u := range rlk[j] {
|
||||
newevakey.evakey.evakey[j][u][0] = rlk[j][u][0].CopyNew()
|
||||
newevakey.evakey.evakey[j][u][1] = rlk[j][u][1].CopyNew()
|
||||
}
|
||||
}
|
||||
|
||||
return newevakey, nil
|
||||
}
|
||||
|
||||
// NewSwitchingKey generated a new keyswitching key, that will re-encrypt a ciphertext encrypted under the input key to the output key.
|
||||
// Here bitdecomp plays a role in the added noise if the scale of the input is smaller than the maximum size between the modulies.
|
||||
func (keygen *KeyGenerator) NewSwitchingKey(sk_input, sk_output *SecretKey, bitDecomp uint64) (newevakey *SwitchingKey, err error) {
|
||||
|
||||
if err = keygen.check_sk(sk_input); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = keygen.check_sk(sk_output); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keygen.context.Sub(sk_input.Get(), sk_output.Get(), keygen.polypool)
|
||||
newevakey = newswitchingkey(keygen.ckkscontext, keygen.polypool, sk_output.Get(), bitDecomp)
|
||||
keygen.polypool.Zero()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// NewRotationKeys generates a new instance of rotationkeys, with the provided rotation to the left, right and conjugation if asked.
|
||||
// Here bitdecomp plays a role in the added noise if the scale of the input is smaller than the maximum size between the modulies.
|
||||
func (keygen *KeyGenerator) NewRotationKeys(sk_output *SecretKey, bitDecomp uint64, rotLeft []uint64, rotRight []uint64, conjugate bool) (rotKey *RotationKey, err error) {
|
||||
|
||||
if err = keygen.check_sk(sk_output); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if bitDecomp > keygen.ckkscontext.maxBit || bitDecomp == 0 {
|
||||
bitDecomp = keygen.ckkscontext.maxBit
|
||||
}
|
||||
|
||||
rotKey = new(RotationKey)
|
||||
rotKey.ckkscontext = keygen.ckkscontext
|
||||
|
||||
if rotLeft != nil {
|
||||
rotKey.evakey_rot_col_L = make(map[uint64]*SwitchingKey)
|
||||
for _, n := range rotLeft {
|
||||
if rotKey.evakey_rot_col_L[n] == nil && n != 0 {
|
||||
rotKey.evakey_rot_col_L[n] = genrotkey(keygen, sk_output.Get(), keygen.ckkscontext.galElRotColLeft[n], bitDecomp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if rotRight != nil {
|
||||
rotKey.evakey_rot_col_R = make(map[uint64]*SwitchingKey)
|
||||
for _, n := range rotRight {
|
||||
if rotKey.evakey_rot_col_R[n] == nil && n != 0 {
|
||||
rotKey.evakey_rot_col_R[n] = genrotkey(keygen, sk_output.Get(), keygen.ckkscontext.galElRotColRight[n], bitDecomp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if conjugate {
|
||||
rotKey.evakey_rot_row = genrotkey(keygen, sk_output.Get(), keygen.ckkscontext.galElRotRow, bitDecomp)
|
||||
}
|
||||
|
||||
return rotKey, nil
|
||||
|
||||
}
|
||||
|
||||
// NewRotationkeysPow2 generates a new rotation key with all the power of two rotation to the left and right, as well as the conjugation
|
||||
// key if asked. Here bitdecomp plays a role in the added noise if the scale of the input is smaller than the maximum size between the modulies.
|
||||
func (keygen *KeyGenerator) NewRotationKeysPow2(sk_output *SecretKey, bitDecomp uint64, conjugate bool) (rotKey *RotationKey, err error) {
|
||||
|
||||
if err = keygen.check_sk(sk_output); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if bitDecomp > keygen.ckkscontext.maxBit || bitDecomp == 0 {
|
||||
bitDecomp = keygen.ckkscontext.maxBit
|
||||
}
|
||||
|
||||
rotKey = new(RotationKey)
|
||||
rotKey.ckkscontext = keygen.ckkscontext
|
||||
|
||||
rotKey.evakey_rot_col_L = make(map[uint64]*SwitchingKey)
|
||||
rotKey.evakey_rot_col_R = make(map[uint64]*SwitchingKey)
|
||||
|
||||
for n := uint64(1); n < rotKey.ckkscontext.n>>1; n <<= 1 {
|
||||
|
||||
rotKey.evakey_rot_col_L[n] = genrotkey(keygen, sk_output.Get(), keygen.ckkscontext.galElRotColLeft[n], bitDecomp)
|
||||
rotKey.evakey_rot_col_R[n] = genrotkey(keygen, sk_output.Get(), keygen.ckkscontext.galElRotColRight[n], bitDecomp)
|
||||
}
|
||||
|
||||
if conjugate {
|
||||
rotKey.evakey_rot_row = genrotkey(keygen, sk_output.Get(), keygen.ckkscontext.galElRotRow, bitDecomp)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func genrotkey(keygen *KeyGenerator, sk_output *ring.Poly, gen, bitDecomp uint64) (switchingkey *SwitchingKey) {
|
||||
|
||||
ring.PermuteNTT(sk_output, gen, keygen.polypool)
|
||||
keygen.context.Sub(keygen.polypool, sk_output, keygen.polypool)
|
||||
switchingkey = newswitchingkey(keygen.ckkscontext, keygen.polypool, sk_output, bitDecomp)
|
||||
keygen.polypool.Zero()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func newswitchingkey(ckkscontext *CkksContext, sk_in, sk_out *ring.Poly, bitDecomp uint64) (switchingkey *SwitchingKey) {
|
||||
|
||||
if bitDecomp > ckkscontext.maxBit || bitDecomp == 0 {
|
||||
bitDecomp = ckkscontext.maxBit
|
||||
}
|
||||
|
||||
switchingkey = new(SwitchingKey)
|
||||
|
||||
context := ckkscontext.keyscontext
|
||||
|
||||
switchingkey.bitDecomp = uint64(bitDecomp)
|
||||
|
||||
mredParams := context.GetMredParams()
|
||||
|
||||
// delta_sk = sk_input - sk_output = GaloisEnd(sk_output, rotation) - sk_output
|
||||
var bitLog uint64
|
||||
|
||||
switchingkey.evakey = make([][][2]*ring.Poly, len(context.Modulus))
|
||||
|
||||
for i, qi := range context.Modulus {
|
||||
|
||||
bitLog = uint64(math.Ceil(float64(bits.Len64(qi)) / float64(bitDecomp)))
|
||||
|
||||
switchingkey.evakey[i] = make([][2]*ring.Poly, bitLog)
|
||||
|
||||
for j := uint64(0); j < bitLog; j++ {
|
||||
|
||||
// e
|
||||
switchingkey.evakey[i][j][0] = ckkscontext.gaussianSampler.SampleNTTNew()
|
||||
// a
|
||||
switchingkey.evakey[i][j][1] = context.NewUniformPoly()
|
||||
|
||||
// e + sk_in * (qiBarre*qiStar) * 2^w
|
||||
// (qiBarre*qiStar)%qi = 1, else 0
|
||||
for w := uint64(0); w < context.N; w++ {
|
||||
switchingkey.evakey[i][j][0].Coeffs[i][w] += PowerOf2(sk_in.Coeffs[i][w], bitDecomp*j, qi, mredParams[i])
|
||||
}
|
||||
|
||||
// sk_in * (qiBarre*qiStar) * 2^w - a*sk + e
|
||||
context.MulCoeffsMontgomeryAndSub(switchingkey.evakey[i][j][1], sk_out, switchingkey.evakey[i][j][0])
|
||||
|
||||
context.MForm(switchingkey.evakey[i][j][0], switchingkey.evakey[i][j][0])
|
||||
context.MForm(switchingkey.evakey[i][j][1], switchingkey.evakey[i][j][1])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
358
ckks/plaintext.go
Normal file
358
ckks/plaintext.go
Normal file
@@ -0,0 +1,358 @@
|
||||
package ckks
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
"math/bits"
|
||||
"math/cmplx"
|
||||
)
|
||||
|
||||
// The plaintext is a ring of N coefficients with two contexts.
|
||||
// The first context is defined by the BFV parameters. The second
|
||||
// context defines a NTT around its modulus it it permits it.
|
||||
|
||||
type Plaintext BigPoly
|
||||
|
||||
// NewPlaintext creates a new plaintext of level level and scale scale.
|
||||
func (ckkscontext *CkksContext) NewPlaintext(level uint64, scale uint64) *Plaintext {
|
||||
plaintext := new(Plaintext)
|
||||
plaintext.ckkscontext = ckkscontext
|
||||
plaintext.value = []*ring.Poly{ckkscontext.contextLevel[level].NewPoly()}
|
||||
plaintext.scale = scale
|
||||
plaintext.currentModulus = ring.Copy(ckkscontext.contextLevel[level].ModulusBigint)
|
||||
plaintext.isNTT = true
|
||||
return plaintext
|
||||
}
|
||||
|
||||
// Value returns the value (polynomial) of the plaintext.
|
||||
func (P *Plaintext) Value() []*ring.Poly {
|
||||
return P.value
|
||||
}
|
||||
|
||||
// SetValue sets the value (polynomial) of the plaintext to the provided value.
|
||||
func (P *Plaintext) SetValue(value []*ring.Poly) {
|
||||
P.value = value
|
||||
}
|
||||
|
||||
// Resize does nothing on a plaintext since it is always of degree 0.
|
||||
func (P *Plaintext) Resize(degree uint64) {
|
||||
|
||||
}
|
||||
|
||||
// CkksContext returns the ckkscontext of the plaintext.
|
||||
func (P *Plaintext) CkksContext() *CkksContext {
|
||||
return P.ckkscontext
|
||||
}
|
||||
|
||||
// SetCkksContext assigns a new ckkscontext to the plaintext.
|
||||
func (P *Plaintext) SetCkksContext(ckkscontext *CkksContext) {
|
||||
P.ckkscontext = ckkscontext
|
||||
}
|
||||
|
||||
// CurrentModulus returns the current modulus of the plaintext.
|
||||
// This variable is only used during the decoding.
|
||||
func (P *Plaintext) CurrentModulus() *ring.Int {
|
||||
return P.currentModulus
|
||||
}
|
||||
|
||||
// SetCurrentModulus sets the current modulus to the provided values.
|
||||
// This variable is only used during the decoding.
|
||||
func (P *Plaintext) SetCurrentModulus(modulus *ring.Int) {
|
||||
P.currentModulus = ring.Copy(modulus)
|
||||
}
|
||||
|
||||
// Degree returns the degree of the plaintext,
|
||||
// this value should always be zero.
|
||||
func (P *Plaintext) Degree() uint64 {
|
||||
return uint64(len(P.value) - 1)
|
||||
}
|
||||
|
||||
// Level returns the current level of the plaintext.
|
||||
func (P *Plaintext) Level() uint64 {
|
||||
return uint64(len(P.value[0].Coeffs) - 1)
|
||||
}
|
||||
|
||||
// Scale returns the current scale of the plaintext (in log2).
|
||||
func (P *Plaintext) Scale() uint64 {
|
||||
return P.scale
|
||||
}
|
||||
|
||||
// SetScale sets the scale of the plaintext to the provided value (in log2).
|
||||
func (P *Plaintext) SetScale(scale uint64) {
|
||||
P.scale = scale
|
||||
}
|
||||
|
||||
// IsNTT returns true or false depending on if the plaintext is in the NTT domain or not.
|
||||
func (P *Plaintext) IsNTT() bool {
|
||||
return P.isNTT
|
||||
}
|
||||
|
||||
// SetIsNTT sets the isNTT value of the plaintext to the provided value.
|
||||
func (P *Plaintext) SetIsNTT(isNTT bool) {
|
||||
P.isNTT = isNTT
|
||||
}
|
||||
|
||||
// NTT applies the NTT transform to a plaintext and returns the result on the receiver element.
|
||||
// Can only be used if the plaintext is not already in the NTT domain.
|
||||
func (P *Plaintext) NTT(ct0 CkksElement) {
|
||||
|
||||
if P.isNTT != true {
|
||||
for i := range ct0.Value() {
|
||||
P.ckkscontext.contextLevel[P.Level()].NTT(P.value[i], ct0.Value()[i])
|
||||
}
|
||||
ct0.SetIsNTT(true)
|
||||
}
|
||||
}
|
||||
|
||||
// InvNTT applies the inverse NTT transform to a plaintext and returns the result on the receiver element.
|
||||
// Can only be used it the plaintext is in the NTT domain
|
||||
func (P *Plaintext) InvNTT(ct0 CkksElement) {
|
||||
|
||||
if P.isNTT != false {
|
||||
for i := range ct0.Value() {
|
||||
P.ckkscontext.contextLevel[P.Level()].InvNTT(P.value[i], ct0.Value()[i])
|
||||
}
|
||||
ct0.SetIsNTT(false)
|
||||
}
|
||||
}
|
||||
|
||||
// CopyNew creates a new plaintext with the same value and same parameters.
|
||||
func (P *Plaintext) CopyNew() CkksElement {
|
||||
|
||||
PCopy := new(Plaintext)
|
||||
|
||||
PCopy.value = make([]*ring.Poly, 1)
|
||||
PCopy.value[0] = P.value[0].CopyNew()
|
||||
PCopy.ckkscontext = P.ckkscontext
|
||||
P.CopyParams(PCopy)
|
||||
|
||||
return PCopy
|
||||
}
|
||||
|
||||
// Copy copies the value and parameters of the reference plaintext ot the receiver plaintext.
|
||||
func (P *Plaintext) Copy(PCopy CkksElement) error {
|
||||
|
||||
if !checkContext([]CkksElement{P, PCopy}) {
|
||||
return errors.New("input ciphertext are not using the same ckkscontext")
|
||||
}
|
||||
|
||||
P.value[0].Copy(PCopy.Value()[0])
|
||||
|
||||
P.CopyParams(PCopy)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CopyParams copies the parameters of the reference plaintext to the receiver plaintext.
|
||||
func (P *Plaintext) CopyParams(ckkselement CkksElement) {
|
||||
|
||||
ckkselement.SetCurrentModulus(P.CurrentModulus())
|
||||
ckkselement.SetScale(P.Scale())
|
||||
ckkselement.SetIsNTT(P.IsNTT())
|
||||
}
|
||||
|
||||
// EncodeFloat encode a float64 slice of at most N/2 values.
|
||||
func (plaintext *Plaintext) EncodeFloat(coeffs []float64) error {
|
||||
|
||||
if len(coeffs) > (len(plaintext.ckkscontext.indexMatrix)>>1)/int(plaintext.ckkscontext.gap) {
|
||||
return errors.New("error : invalid input to encode (number of coefficients must be smaller or equal to the context)")
|
||||
}
|
||||
|
||||
if len(plaintext.value[0].Coeffs[0]) != len(plaintext.ckkscontext.indexMatrix) {
|
||||
return errors.New("error : invalid plaintext to receive encoding (number of coefficients does not match the context of the encoder")
|
||||
}
|
||||
|
||||
values := make([]complex128, len(coeffs)*int(plaintext.ckkscontext.gap))
|
||||
|
||||
for i := range coeffs {
|
||||
|
||||
values[i*int(plaintext.ckkscontext.gap)] = complex(coeffs[i], 0)
|
||||
|
||||
for j := 0; j < int(plaintext.ckkscontext.gap)-1; j++ {
|
||||
values[i*int(plaintext.ckkscontext.gap)+j+1] = complex(0, 0)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
encodeFromComplex(values, plaintext)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// EncodeFloat encode a complex128 slice of at most N/2 values.
|
||||
func (plaintext *Plaintext) EncodeComplex(coeffs []complex128) error {
|
||||
|
||||
if len(coeffs) > (len(plaintext.ckkscontext.indexMatrix)>>1)/int(plaintext.ckkscontext.gap) {
|
||||
return errors.New("error : invalid input to encode (number of coefficients must be smaller or equal to the context)")
|
||||
}
|
||||
|
||||
if len(plaintext.value[0].Coeffs[0]) != len(plaintext.ckkscontext.indexMatrix) {
|
||||
return errors.New("error : invalid plaintext to receive encoding (number of coefficients does not match the context of the encoder")
|
||||
}
|
||||
|
||||
values := make([]complex128, len(coeffs)*int(plaintext.ckkscontext.gap))
|
||||
|
||||
for i := range coeffs {
|
||||
values[i*int(plaintext.ckkscontext.gap)] = coeffs[i]
|
||||
|
||||
for j := 0; j < int(plaintext.ckkscontext.gap)-1; j++ {
|
||||
values[i*int(plaintext.ckkscontext.gap)+j+1] = complex(0, 0)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
encodeFromComplex(values, plaintext)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecodeFloat decodes the plaintext to a slice of float64 values of size at most N/2.
|
||||
func (plaintext *Plaintext) DecodeFloat() (res []float64) {
|
||||
|
||||
values := decodeToComplex(plaintext.value[0], plaintext.currentModulus, plaintext.ckkscontext.contextLevel[plaintext.Level()], plaintext.ckkscontext.roots, plaintext.scale)
|
||||
|
||||
res = make([]float64, int(plaintext.ckkscontext.slots)/int(plaintext.ckkscontext.gap))
|
||||
|
||||
for i := range res {
|
||||
res[i] = real(values[plaintext.ckkscontext.indexMatrix[i*int(plaintext.ckkscontext.gap)]])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeFloat decodes the plaintext to a slice of complex128 values of size at most N/2.
|
||||
func (plaintext *Plaintext) DecodeComplex() (res []complex128) {
|
||||
|
||||
values := decodeToComplex(plaintext.value[0], plaintext.currentModulus, plaintext.ckkscontext.contextLevel[plaintext.Level()], plaintext.ckkscontext.roots, plaintext.scale)
|
||||
|
||||
res = make([]complex128, int(plaintext.ckkscontext.slots)/int(plaintext.ckkscontext.gap))
|
||||
|
||||
for i := range res {
|
||||
res[i] = values[plaintext.ckkscontext.indexMatrix[i*int(plaintext.ckkscontext.gap)]]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func encodeFromComplex(coeffs []complex128, plaintext *Plaintext) {
|
||||
|
||||
values := make([]complex128, plaintext.ckkscontext.n)
|
||||
|
||||
for i := 0; i < len(coeffs); i++ {
|
||||
values[plaintext.ckkscontext.indexMatrix[i]] = coeffs[i]
|
||||
values[plaintext.ckkscontext.indexMatrix[i+int(plaintext.ckkscontext.slots)]] = cmplx.Conj(coeffs[i])
|
||||
}
|
||||
|
||||
invfft(values, plaintext.ckkscontext.inv_roots)
|
||||
|
||||
for i, qi := range plaintext.ckkscontext.modulie {
|
||||
|
||||
for j := uint64(0); j < plaintext.ckkscontext.n; j++ {
|
||||
|
||||
tmp := real(values[j]) / float64(plaintext.ckkscontext.n)
|
||||
|
||||
if tmp != 0 {
|
||||
plaintext.value[0].Coeffs[i][j] = scaleUp(tmp, plaintext.scale, qi)
|
||||
} else {
|
||||
plaintext.value[0].Coeffs[i][j] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plaintext.ckkscontext.contextLevel[plaintext.Level()].NTT(plaintext.value[0], plaintext.value[0])
|
||||
}
|
||||
|
||||
func decodeToComplex(pol *ring.Poly, Q *ring.Int, context *ring.Context, roots []complex128, scale uint64) (values []complex128) {
|
||||
|
||||
tmp := context.NewPoly()
|
||||
|
||||
context.InvNTT(pol, tmp)
|
||||
|
||||
bigint_coeffs := context.PolyToBigint(tmp)
|
||||
|
||||
Q_half := new(ring.Int)
|
||||
Q_half.SetBigInt(Q)
|
||||
Q_half.Rsh(Q_half, 1)
|
||||
|
||||
var sign int
|
||||
values = make([]complex128, context.N)
|
||||
for i := range bigint_coeffs {
|
||||
|
||||
// Centers the value arounds the current modulus
|
||||
bigint_coeffs[i].Mod(bigint_coeffs[i], Q)
|
||||
sign = bigint_coeffs[i].Compare(Q_half)
|
||||
if sign == 1 || sign == 0 {
|
||||
bigint_coeffs[i].Sub(bigint_coeffs[i], Q)
|
||||
}
|
||||
|
||||
values[i] = complex(scaleDown(bigint_coeffs[i], scale), 0)
|
||||
}
|
||||
|
||||
fft(values, roots)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func invfft(values, inv_roots []complex128) {
|
||||
|
||||
var logN, mm, k_start, k_end, h, t uint64
|
||||
var u, v, psi complex128
|
||||
|
||||
logN = uint64(bits.Len64(uint64(len(values))) - 1)
|
||||
|
||||
t = 1
|
||||
|
||||
for i := uint64(0); i < logN; i++ {
|
||||
|
||||
mm = 1 << (logN - i)
|
||||
k_start = 0
|
||||
h = mm >> 1
|
||||
|
||||
for j := uint64(0); j < h; j++ {
|
||||
|
||||
k_end = k_start + t
|
||||
psi = inv_roots[h+j]
|
||||
|
||||
for k := k_start; k < k_end; k++ {
|
||||
|
||||
u = values[k]
|
||||
v = values[k+t]
|
||||
values[k] = u + v
|
||||
values[k+t] = (u - v) * psi
|
||||
}
|
||||
|
||||
k_start += (t << 1)
|
||||
}
|
||||
|
||||
t <<= 1
|
||||
}
|
||||
}
|
||||
|
||||
func fft(values, roots []complex128) {
|
||||
|
||||
var logN, t, mm, j1, j2 uint64
|
||||
var psi, u, v complex128
|
||||
|
||||
t = uint64(len(values))
|
||||
|
||||
logN = uint64(bits.Len64(t) - 1)
|
||||
|
||||
for i := uint64(0); i < logN; i++ {
|
||||
mm = 1 << i
|
||||
t >>= 1
|
||||
|
||||
for j := uint64(0); j < mm; j++ {
|
||||
j1 = 2 * j * t
|
||||
j2 = j1 + t - 1
|
||||
psi = roots[mm+j]
|
||||
|
||||
for k := j1; k < j2+1; k++ {
|
||||
|
||||
u = values[k]
|
||||
v = values[k+t] * psi
|
||||
values[k] = u + v
|
||||
values[k+t] = u - v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
301
ckks/utils.go
Normal file
301
ckks/utils.go
Normal file
@@ -0,0 +1,301 @@
|
||||
package ckks
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
"golang.org/x/crypto/blake2b"
|
||||
"math"
|
||||
"math/big"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// Multiplies x by 2^n and returns the result mod q
|
||||
// Unaffected by overflows, arbitrary n allowed.
|
||||
// Expects inputs in the range of uint64.
|
||||
func scaleUp(x float64, n, q uint64) uint64 {
|
||||
|
||||
var a uint64
|
||||
if n < 53 {
|
||||
a = n
|
||||
} else {
|
||||
a = 53
|
||||
}
|
||||
|
||||
var is_negative bool
|
||||
|
||||
if x < 0 {
|
||||
is_negative = true
|
||||
x *= -1
|
||||
}
|
||||
|
||||
x_int := uint64(x)
|
||||
x_flo := x - float64(x_int)
|
||||
x_int %= q
|
||||
|
||||
for i := uint64(0); i < a; i++ { // stops at 53 to avoid an overflow of the float
|
||||
x_int <<= 1
|
||||
x_flo *= 2
|
||||
if x_int >= q {
|
||||
x_int -= q
|
||||
}
|
||||
}
|
||||
|
||||
x_int += uint64(math.Round(x_flo))
|
||||
x_int %= q
|
||||
|
||||
if n > 53 { // continues with int and and float merged without a risk of overflow
|
||||
for i := a; i < n; i++ {
|
||||
x_int <<= 1
|
||||
if x_int >= q {
|
||||
x_int -= q
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if is_negative {
|
||||
return q - x_int
|
||||
} else {
|
||||
return x_int
|
||||
}
|
||||
}
|
||||
|
||||
// Divides x by n^2, returns a float
|
||||
func scaleDown(coeff *ring.Int, n uint64) (x float64) {
|
||||
|
||||
if n > 53 { // if n > float64 precision, then first reduce the integer to 53 bits, and the scales down
|
||||
coeff.Rsh(coeff, n-53)
|
||||
n -= (n - 53)
|
||||
}
|
||||
|
||||
x, _ = new(big.Float).SetInt(&coeff.Value).Float64()
|
||||
|
||||
x /= float64(uint64(1 << n))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func Hash(data []uint64) (value []byte, err error) {
|
||||
hash, err := blake2b.New512(nil)
|
||||
buff := make([]byte, 8)
|
||||
for _, x := range data {
|
||||
binary.BigEndian.PutUint64(buff, x)
|
||||
hash.Write(buff)
|
||||
}
|
||||
value = hash.Sum(nil)
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func VerifyHash(hash0, hash1 []byte) bool {
|
||||
if res := bytes.Compare(hash0, hash1); res != 0 {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func getLevels(inputs []CkksElement) (levels []uint64) {
|
||||
|
||||
levels = make([]uint64, len(inputs))
|
||||
|
||||
for i := range inputs {
|
||||
levels[i] = inputs[i].Level()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func checkLevels(inputs []CkksElement) (uint64, error) {
|
||||
|
||||
levels := getLevels(inputs)
|
||||
|
||||
for i := 1; i < len(levels); i++ {
|
||||
if levels[0] != levels[i] {
|
||||
return 0, errors.New("error : inputs need to be on the same level")
|
||||
}
|
||||
}
|
||||
|
||||
return levels[0], nil
|
||||
}
|
||||
|
||||
func checkContext(inputs []CkksElement) bool {
|
||||
|
||||
var value []byte
|
||||
|
||||
value = inputs[0].CkksContext().checksum
|
||||
|
||||
for i := range inputs[1:] {
|
||||
|
||||
if res := VerifyHash(value, inputs[i].CkksContext().checksum); res != true {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func GenerateNTTPrime(logQ, logN uint64) (uint64, error) {
|
||||
|
||||
if logQ > 62 {
|
||||
return 0, errors.New("error : logQ must be between 1 and 62")
|
||||
}
|
||||
|
||||
var x, Qpow2, _2N uint64
|
||||
|
||||
Qpow2 = 1 << logQ
|
||||
|
||||
_2N = 2 << logN
|
||||
|
||||
x = Qpow2 + 1
|
||||
|
||||
for ring.IsPrime(x) != true {
|
||||
x += _2N
|
||||
}
|
||||
|
||||
return x, nil
|
||||
}
|
||||
|
||||
func GeneratePrimesList(logQ, logN, logP uint64) ([]uint64, error) {
|
||||
|
||||
if logQ > 62 {
|
||||
return nil, errors.New("error : logQ must be between 1 and 62")
|
||||
}
|
||||
|
||||
var x, Qpow2, _2N, precision uint64
|
||||
|
||||
primes := []uint64{}
|
||||
|
||||
Qpow2 = 1 << logQ
|
||||
|
||||
_2N = 2 << logN
|
||||
|
||||
precision = 1 << logP
|
||||
|
||||
x = Qpow2 + 1
|
||||
|
||||
// Gets x + 1 + k*2N = 1 mod 2N to the precision upperbound
|
||||
for (1-(float64(x)/float64(Qpow2)))*float64(precision) > -1 {
|
||||
x += _2N
|
||||
}
|
||||
|
||||
// Walks backward this time checking if x + 1 + k*2N is prime
|
||||
for (1-(float64(x)/float64(Qpow2)))*float64(precision) < 1 {
|
||||
|
||||
x -= _2N
|
||||
|
||||
if ring.IsPrime(x) {
|
||||
primes = append(primes, x)
|
||||
}
|
||||
}
|
||||
|
||||
return primes, nil
|
||||
}
|
||||
|
||||
// Generates CKKS Primes byed on logQ = size of the primes, logN = size of N and level, the number
|
||||
// of levels we require. Will return all the appropriate primes, up to the number of level, with the
|
||||
// best avaliable precision for the given level.
|
||||
// TODO : choose between sorting the primes by size or by precision
|
||||
func GenerateCKKSPrimes(logQ, logN, level uint64) ([]uint64, uint64, error) {
|
||||
|
||||
var err error
|
||||
|
||||
if logQ > 62 {
|
||||
return nil, 0, errors.New("error : logQ must be between 1 and 62")
|
||||
}
|
||||
|
||||
var precision uint64
|
||||
|
||||
var primes []uint64
|
||||
|
||||
precision = logQ - 1
|
||||
|
||||
for precision > 0 {
|
||||
|
||||
primes, err = GeneratePrimesList(logQ, logN, precision)
|
||||
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if len(primes) >= int(level) {
|
||||
return primes[:level], precision, nil
|
||||
}
|
||||
|
||||
precision -= 1
|
||||
}
|
||||
|
||||
return nil, 0, nil
|
||||
}
|
||||
|
||||
func EqualSlice(a, b []uint64) bool {
|
||||
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range a {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func min(values []uint64) (r uint64) {
|
||||
r = values[0]
|
||||
for _, i := range values[1:] {
|
||||
if i < r {
|
||||
r = i
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func max(values []uint64) (r uint64) {
|
||||
r = values[0]
|
||||
for _, i := range values[1:] {
|
||||
if i > r {
|
||||
r = i
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func bitReverse64(index, bitLen uint64) uint64 {
|
||||
return bits.Reverse64(index) >> (64 - bitLen)
|
||||
}
|
||||
|
||||
func hammingWeight64(x uint64) uint64 {
|
||||
x -= (x >> 1) & 0x5555555555555555
|
||||
x = (x & 0x3333333333333333) + ((x >> 2) & 0x3333333333333333)
|
||||
x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0f
|
||||
return ((x * 0x0101010101010101) & 0xffffffffffffffff) >> 56
|
||||
}
|
||||
|
||||
func modexp(x, e, p uint64) (result uint64) {
|
||||
params := ring.BRedParams(p)
|
||||
result = 1
|
||||
for i := e; i > 0; i >>= 1 {
|
||||
if i&1 == 1 {
|
||||
result = ring.BRed(result, x, p, params)
|
||||
}
|
||||
x = ring.BRed(x, x, p, params)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Returns (x*2^n)%q where x is in montgomery form
|
||||
func PowerOf2(x, n, q, qInv uint64) (r uint64) {
|
||||
ahi, alo := x>>(64-n), x<<n
|
||||
R := alo * qInv
|
||||
H, _ := bits.Mul64(R, q)
|
||||
r = ahi - H + q
|
||||
if r >= q {
|
||||
r -= q
|
||||
}
|
||||
return
|
||||
}
|
||||
148
dbfv/collective_CRS.go
Normal file
148
dbfv/collective_CRS.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package dbfv
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
"golang.org/x/crypto/blake2b"
|
||||
"hash"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
type PRNG struct {
|
||||
clock uint64
|
||||
seed []byte
|
||||
hash hash.Hash
|
||||
}
|
||||
|
||||
// NewPRNG creates a new instance of PRNG
|
||||
// Accepts an optional key, if no key, call with key=nill
|
||||
func NewPRNG(key []byte) (*PRNG, error) {
|
||||
var err error
|
||||
prng := new(PRNG)
|
||||
prng.clock = 0
|
||||
prng.hash, err = blake2b.New512(key)
|
||||
return prng, err
|
||||
}
|
||||
|
||||
// GetClock returns the value of the clock cycle of the PRNG
|
||||
func (prng *PRNG) GetClock() uint64 {
|
||||
return prng.clock
|
||||
}
|
||||
|
||||
// Seed resets the current state of the PRNG (without changing the
|
||||
// optional key) and seeds it with the given bytes.
|
||||
// Seed will also reset the clock cycle to 0.
|
||||
func (prng *PRNG) Seed(seed []byte) {
|
||||
prng.hash.Reset()
|
||||
prng.seed = seed[:]
|
||||
prng.hash.Write(seed)
|
||||
prng.clock = 0
|
||||
}
|
||||
|
||||
func (prng *PRNG) GetSeed() []byte {
|
||||
return prng.seed[:]
|
||||
}
|
||||
|
||||
// Clock returns the right part of the digest value
|
||||
// of the current PRNG state and reseeds the PRNG with the
|
||||
// left part of the digest value. Increases the clock cycle by 1.
|
||||
func (prng *PRNG) Clock() []byte {
|
||||
tmp := prng.hash.Sum(nil)
|
||||
prng.hash.Write(tmp[:32])
|
||||
prng.clock += 1
|
||||
return tmp[32:]
|
||||
}
|
||||
|
||||
// Sets the clock cycle of the PRNG to a given number. Returns an error if
|
||||
// the target clock cycle is smaller than the current clock cycle.
|
||||
func (prng *PRNG) SetClock(n uint64) error {
|
||||
if prng.clock > n {
|
||||
return errors.New("error : cannot set prng clock to a previous state")
|
||||
}
|
||||
var tmp []byte
|
||||
for prng.clock != n {
|
||||
tmp = prng.hash.Sum(nil)
|
||||
prng.hash.Write(tmp[:32])
|
||||
prng.clock += 1
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Common Reference Polynomial Generator
|
||||
type CRPGenerator struct {
|
||||
prng *PRNG
|
||||
context *ring.Context
|
||||
masks []uint64
|
||||
}
|
||||
|
||||
func NewCRPGenerator(key []byte, context *ring.Context) (*CRPGenerator, error) {
|
||||
var err error
|
||||
crpgenerator := new(CRPGenerator)
|
||||
crpgenerator.prng, err = NewPRNG(key)
|
||||
crpgenerator.context = context
|
||||
crpgenerator.masks = make([]uint64, len(context.Modulus))
|
||||
for i, qi := range context.Modulus {
|
||||
crpgenerator.masks[i] = (1 << uint64(bits.Len64(qi))) - 1
|
||||
}
|
||||
return crpgenerator, err
|
||||
}
|
||||
|
||||
func (crpgenerator *CRPGenerator) GetClock() uint64 {
|
||||
return crpgenerator.prng.clock
|
||||
}
|
||||
|
||||
func (crpgenerator *CRPGenerator) Seed(seed []byte) {
|
||||
crpgenerator.prng.Seed(seed)
|
||||
}
|
||||
|
||||
func (crpgenerator *CRPGenerator) GetSeed() []byte {
|
||||
return crpgenerator.prng.seed[:]
|
||||
}
|
||||
|
||||
func (crpgenerator *CRPGenerator) SetClock(n uint64) error {
|
||||
if err := crpgenerator.prng.SetClock(n); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (crpgenerator *CRPGenerator) Clock() *ring.Poly {
|
||||
var coeff uint64
|
||||
var randomBytes []byte
|
||||
|
||||
crp := crpgenerator.context.NewPoly()
|
||||
|
||||
// Starts with 32 random bytes from the prng
|
||||
randomBytes = crpgenerator.prng.Clock()
|
||||
|
||||
for i := uint64(0); i < crpgenerator.context.N; i++ {
|
||||
|
||||
for j, qi := range crpgenerator.context.Modulus {
|
||||
|
||||
for {
|
||||
// If not enough randomBytes, generate 32 more random bytes from the PRNG
|
||||
if len(randomBytes) < 8 {
|
||||
randomBytes = crpgenerator.prng.Clock()
|
||||
}
|
||||
|
||||
// Converts 4 randomBytes into an uint64 of at most 60 bits (maximum size of the modulus)
|
||||
coeff = binary.BigEndian.Uint64(randomBytes[:8]) & crpgenerator.masks[j]
|
||||
|
||||
// Drops the used bytes
|
||||
randomBytes = randomBytes[8:]
|
||||
|
||||
// If coeff is smaller than qi, breaks
|
||||
if coeff < qi {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Assigns coeff to the ring crp coefficient
|
||||
crp.Coeffs[j][i] = coeff
|
||||
}
|
||||
}
|
||||
|
||||
return crp
|
||||
}
|
||||
215
dbfv/collective_EKG.go
Normal file
215
dbfv/collective_EKG.go
Normal file
@@ -0,0 +1,215 @@
|
||||
package dbfv
|
||||
|
||||
import (
|
||||
"github.com/lca1/lattigo/bfv"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
"math"
|
||||
)
|
||||
|
||||
type EkgProtocol struct {
|
||||
context *ring.Context
|
||||
gaussianSampler *ring.KYSampler
|
||||
bitDecomp uint64
|
||||
bitLog uint64
|
||||
polypool *ring.Poly
|
||||
}
|
||||
|
||||
func NewEkgProtocol(context *ring.Context, bitDecomp uint64) *EkgProtocol {
|
||||
ekg := new(EkgProtocol)
|
||||
ekg.context = context
|
||||
ekg.gaussianSampler = context.NewKYSampler(3.19, 19)
|
||||
ekg.bitDecomp = bitDecomp
|
||||
ekg.bitLog = uint64(math.Ceil(float64(60) / float64(bitDecomp)))
|
||||
ekg.polypool = context.NewPoly()
|
||||
return ekg
|
||||
}
|
||||
|
||||
// Ephemeral Key u (needs to be stored among the 3 first round)
|
||||
func (ekg *EkgProtocol) NewEphemeralKey() *ring.Poly {
|
||||
ephemeralKey := ekg.context.NewTernaryPoly()
|
||||
ekg.context.NTT(ephemeralKey, ephemeralKey)
|
||||
ekg.context.MForm(ephemeralKey, ephemeralKey)
|
||||
return ephemeralKey
|
||||
}
|
||||
|
||||
// Ephemeral Key u (needs to be stored among the 3 first round)
|
||||
func (ekg *EkgProtocol) GenSamples(u, sk *ring.Poly, crp [][]*ring.Poly) (h [][]*ring.Poly) {
|
||||
|
||||
h = make([][]*ring.Poly, len(ekg.context.Modulus))
|
||||
|
||||
mredParams := ekg.context.GetMredParams()
|
||||
|
||||
// Given a base decomposition w_i (here the CRT decomposition)
|
||||
// computes [-u*a_i + s*w_i + e_i]
|
||||
// where a_i = crp_i
|
||||
for i, qi := range ekg.context.Modulus {
|
||||
|
||||
h[i] = make([]*ring.Poly, ekg.bitLog)
|
||||
|
||||
for w := uint64(0); w < ekg.bitLog; w++ {
|
||||
|
||||
// h = e
|
||||
h[i][w] = ekg.gaussianSampler.SampleNTTNew()
|
||||
|
||||
// h = sk*CrtBaseDecompQi + e
|
||||
for j := uint64(0); j < ekg.context.N; j++ {
|
||||
h[i][w].Coeffs[i][j] += bfv.PowerOf2(sk.Coeffs[i][j], ekg.bitDecomp*w, qi, mredParams[i])
|
||||
}
|
||||
|
||||
// h = sk*CrtBaseDecompQi + -u*a + e
|
||||
ekg.context.MulCoeffsMontgomeryAndSub(u, crp[i][w], h[i][w])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Round 2
|
||||
func (ekg *EkgProtocol) Aggregate(sk *ring.Poly, samples [][][]*ring.Poly, crp [][]*ring.Poly) (h [][][2]*ring.Poly) {
|
||||
|
||||
h = make([][][2]*ring.Poly, len(ekg.context.Modulus))
|
||||
|
||||
// Each sample is of the form [-u*a_i + s*w_i + e_i]
|
||||
// So for each element of the base decomposition w_i :
|
||||
for i := range ekg.context.Modulus {
|
||||
|
||||
h[i] = make([][2]*ring.Poly, ekg.bitLog)
|
||||
|
||||
for w := uint64(0); w < ekg.bitLog; w++ {
|
||||
|
||||
// Computes [(sum samples)*sk + e_1i, sk*a + e_2i]
|
||||
|
||||
// First Element
|
||||
h[i][w][0] = samples[0][i][w].CopyNew()
|
||||
|
||||
// Continues with the sum samples
|
||||
for j := 1; j < len(samples); j++ {
|
||||
ekg.context.AddNoMod(h[i][w][0], samples[j][i][w], h[i][w][0])
|
||||
|
||||
if j&7 == 7 {
|
||||
ekg.context.Reduce(h[i][w][0], h[i][w][0])
|
||||
}
|
||||
}
|
||||
|
||||
if (len(samples)-1)&7 != 7 {
|
||||
ekg.context.Reduce(h[i][w][0], h[i][w][0])
|
||||
}
|
||||
|
||||
// (Sum samples) * sk
|
||||
ekg.context.MulCoeffsMontgomery(h[i][w][0], sk, h[i][w][0])
|
||||
|
||||
// (Sum samples) * sk + e_1i
|
||||
ekg.gaussianSampler.SampleNTT(ekg.polypool)
|
||||
ekg.context.Add(h[i][w][0], ekg.polypool, h[i][w][0])
|
||||
|
||||
// Second Element
|
||||
|
||||
// e_2i
|
||||
h[i][w][1] = ekg.gaussianSampler.SampleNTTNew()
|
||||
// s*a + e_2i
|
||||
ekg.context.MulCoeffsMontgomeryAndAdd(sk, crp[i][w], h[i][w][1])
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ekg.polypool.Zero()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Round 3
|
||||
|
||||
//Part 1
|
||||
func (ekg *EkgProtocol) Sum(samples [][][][2]*ring.Poly) (h [][][2]*ring.Poly) {
|
||||
|
||||
h = make([][][2]*ring.Poly, len(ekg.context.Modulus))
|
||||
|
||||
for i := range ekg.context.Modulus {
|
||||
|
||||
h[i] = make([][2]*ring.Poly, ekg.bitLog)
|
||||
|
||||
for w := uint64(0); w < ekg.bitLog; w++ {
|
||||
|
||||
h[i][w][0] = samples[0][i][w][0].CopyNew()
|
||||
h[i][w][1] = samples[0][i][w][1].CopyNew()
|
||||
|
||||
for j := 1; j < len(samples); j++ {
|
||||
ekg.context.AddNoMod(h[i][w][0], samples[j][i][w][0], h[i][w][0])
|
||||
ekg.context.AddNoMod(h[i][w][1], samples[j][i][w][1], h[i][w][1])
|
||||
|
||||
if j&7 == 7 {
|
||||
ekg.context.Reduce(h[i][w][0], h[i][w][0])
|
||||
ekg.context.Reduce(h[i][w][1], h[i][w][1])
|
||||
}
|
||||
}
|
||||
if (len(samples)-1)&7 != 7 {
|
||||
ekg.context.Reduce(h[i][w][0], h[i][w][0])
|
||||
ekg.context.Reduce(h[i][w][1], h[i][w][1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Part 2
|
||||
func (ekg *EkgProtocol) KeySwitch(u, sk *ring.Poly, samples [][][2]*ring.Poly) (h1 [][]*ring.Poly) {
|
||||
|
||||
h1 = make([][]*ring.Poly, len(ekg.context.Modulus))
|
||||
|
||||
// (u_i - s_i)
|
||||
mask := ekg.context.NewPoly()
|
||||
ekg.context.Sub(u, sk, mask)
|
||||
|
||||
for i := range ekg.context.Modulus {
|
||||
|
||||
h1[i] = make([]*ring.Poly, ekg.bitLog)
|
||||
|
||||
for w := uint64(0); w < ekg.bitLog; w++ {
|
||||
|
||||
// (u - s) * (sum [x][s*a_i + e_2i]) + e3i
|
||||
h1[i][w] = ekg.gaussianSampler.SampleNTTNew()
|
||||
ekg.context.MulCoeffsMontgomeryAndAdd(mask, samples[i][w][1], h1[i][w])
|
||||
}
|
||||
}
|
||||
|
||||
return h1
|
||||
}
|
||||
|
||||
// Round 4
|
||||
|
||||
func (ekg *EkgProtocol) ComputeEVK(h1 [][][]*ring.Poly, h [][][2]*ring.Poly) (collectiveEVK [][][2]*ring.Poly) {
|
||||
|
||||
collectiveEVK = make([][][2]*ring.Poly, len(ekg.context.Modulus))
|
||||
|
||||
// collectiveEVK[i][0] = h[i][0] + sum(h1[i])
|
||||
// collectiveEVK[i][1] = h[i][1]
|
||||
for i := range ekg.context.Modulus {
|
||||
|
||||
collectiveEVK[i] = make([][2]*ring.Poly, ekg.bitLog)
|
||||
|
||||
for w := uint64(0); w < ekg.bitLog; w++ {
|
||||
|
||||
collectiveEVK[i][w][0] = h[i][w][0].CopyNew()
|
||||
collectiveEVK[i][w][1] = h[i][w][1].CopyNew()
|
||||
|
||||
for j := range h1 {
|
||||
ekg.context.AddNoMod(collectiveEVK[i][w][0], h1[j][i][w], collectiveEVK[i][w][0])
|
||||
|
||||
if j&7 == 7 {
|
||||
ekg.context.Reduce(collectiveEVK[i][w][0], collectiveEVK[i][w][0])
|
||||
}
|
||||
}
|
||||
|
||||
if (len(h1)-1)&7 != 7 {
|
||||
ekg.context.Reduce(collectiveEVK[i][w][0], collectiveEVK[i][w][0])
|
||||
}
|
||||
|
||||
ekg.context.MForm(collectiveEVK[i][w][0], collectiveEVK[i][w][0])
|
||||
ekg.context.MForm(collectiveEVK[i][w][1], collectiveEVK[i][w][1])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
160
dbfv/collective_EKG_Naive.go
Normal file
160
dbfv/collective_EKG_Naive.go
Normal file
@@ -0,0 +1,160 @@
|
||||
package dbfv
|
||||
|
||||
import (
|
||||
"github.com/lca1/lattigo/bfv"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
"math"
|
||||
)
|
||||
|
||||
type EkgProtocolNaive struct {
|
||||
context *ring.Context
|
||||
gaussianSampler *ring.KYSampler
|
||||
ternarySampler *ring.TernarySampler
|
||||
bitDecomp uint64
|
||||
bitLog uint64
|
||||
polypool *ring.Poly
|
||||
}
|
||||
|
||||
func NewEkgProtocolNaive(context *ring.Context, bitDecomp uint64) *EkgProtocolNaive {
|
||||
ekg := new(EkgProtocolNaive)
|
||||
ekg.context = context
|
||||
ekg.gaussianSampler = context.NewKYSampler(3.19, 19)
|
||||
ekg.ternarySampler = context.NewTernarySampler()
|
||||
ekg.bitDecomp = bitDecomp
|
||||
ekg.bitLog = uint64(math.Ceil(float64(60) / float64(bitDecomp)))
|
||||
ekg.polypool = context.NewPoly()
|
||||
return ekg
|
||||
}
|
||||
|
||||
func (ekg *EkgProtocolNaive) GenSamples(sk *ring.Poly, pk [2]*ring.Poly) (h [][][2]*ring.Poly) {
|
||||
|
||||
h = make([][][2]*ring.Poly, len(ekg.context.Modulus))
|
||||
|
||||
mredParams := ekg.context.GetMredParams()
|
||||
|
||||
for i, qi := range ekg.context.Modulus {
|
||||
|
||||
h[i] = make([][2]*ring.Poly, ekg.bitLog)
|
||||
|
||||
for w := uint64(0); w < ekg.bitLog; w++ {
|
||||
|
||||
// h_0 = e0
|
||||
h[i][w][0] = ekg.gaussianSampler.SampleNTTNew()
|
||||
// h_1 = e1
|
||||
h[i][w][1] = ekg.gaussianSampler.SampleNTTNew()
|
||||
|
||||
// h_0 = e0 + [sk*w*(qiBarre*qiStar)%qi = 1<<w, else 0]
|
||||
for j := uint64(0); j < ekg.context.N; j++ {
|
||||
h[i][w][0].Coeffs[i][j] += bfv.PowerOf2(sk.Coeffs[i][j], ekg.bitDecomp*w, qi, mredParams[i])
|
||||
}
|
||||
|
||||
// u
|
||||
ekg.ternarySampler.SampleMontgomeryNTT(ekg.polypool)
|
||||
|
||||
// h_0 = pk_0 * u + e0 + sk*w*(qiBarre*qiStar)%qi
|
||||
ekg.context.MulCoeffsMontgomeryAndAdd(pk[0], ekg.polypool, h[i][w][0])
|
||||
// h_1 = pk_1 * u + e1 + sk*w*(qiBarre*qiStar)%qi
|
||||
ekg.context.MulCoeffsMontgomeryAndAdd(pk[1], ekg.polypool, h[i][w][1])
|
||||
}
|
||||
}
|
||||
|
||||
ekg.polypool.Zero()
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func (ekg *EkgProtocolNaive) Aggregate(sk *ring.Poly, pk [2]*ring.Poly, samples [][][][2]*ring.Poly) (h [][][2]*ring.Poly) {
|
||||
|
||||
h = make([][][2]*ring.Poly, len(ekg.context.Modulus))
|
||||
|
||||
for i := range h {
|
||||
|
||||
h[i] = make([][2]*ring.Poly, ekg.bitLog)
|
||||
|
||||
for w := uint64(0); w < ekg.bitLog; w++ {
|
||||
|
||||
h[i][w][0] = samples[0][i][w][0].CopyNew()
|
||||
h[i][w][1] = samples[0][i][w][1].CopyNew()
|
||||
|
||||
// h_0 = sum(samples[0])
|
||||
// h_1 = sum(samples[1])
|
||||
for j := 1; j < len(samples); j++ {
|
||||
ekg.context.AddNoMod(h[i][w][0], samples[j][i][w][0], h[i][w][0])
|
||||
ekg.context.AddNoMod(h[i][w][1], samples[j][i][w][1], h[i][w][1])
|
||||
|
||||
if j&7 == 7 {
|
||||
ekg.context.Reduce(h[i][w][0], h[i][w][0])
|
||||
ekg.context.Reduce(h[i][w][1], h[i][w][1])
|
||||
}
|
||||
}
|
||||
|
||||
if (len(samples)-1)&7 == 7 {
|
||||
ekg.context.Reduce(h[i][w][0], h[i][w][0])
|
||||
ekg.context.Reduce(h[i][w][1], h[i][w][1])
|
||||
}
|
||||
|
||||
// h_0 = sum(samples[0]) * sk
|
||||
// h_1 = sum(samples[1]) * sk
|
||||
ekg.context.MulCoeffsMontgomery(h[i][w][0], sk, h[i][w][0])
|
||||
ekg.context.MulCoeffsMontgomery(h[i][w][1], sk, h[i][w][1])
|
||||
|
||||
// v
|
||||
ekg.ternarySampler.SampleMontgomeryNTT(ekg.polypool)
|
||||
|
||||
// h_0 = sum(samples[0]) * sk + pk0 * v
|
||||
ekg.context.MulCoeffsMontgomeryAndAdd(pk[0], ekg.polypool, h[i][w][0])
|
||||
|
||||
// h_1 = sum(samples[1]) * sk + pk1 * v
|
||||
ekg.context.MulCoeffsMontgomeryAndAdd(pk[1], ekg.polypool, h[i][w][1])
|
||||
|
||||
// h_0 = sum(samples[0]) * sk + pk0 * v + e2
|
||||
ekg.gaussianSampler.SampleNTT(ekg.polypool)
|
||||
ekg.context.Add(h[i][w][0], ekg.polypool, h[i][w][0])
|
||||
|
||||
// h_1 = sum(samples[1]) * sk + pk1 * v + e3
|
||||
ekg.gaussianSampler.SampleNTT(ekg.polypool)
|
||||
ekg.context.Add(h[i][w][1], ekg.polypool, h[i][w][1])
|
||||
}
|
||||
}
|
||||
|
||||
ekg.polypool.Zero()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (ekg *EkgProtocolNaive) Finalize(h [][][][2]*ring.Poly) (evaluationKey [][][2]*ring.Poly) {
|
||||
|
||||
evaluationKey = make([][][2]*ring.Poly, len(ekg.context.Modulus))
|
||||
|
||||
for i := range ekg.context.Modulus {
|
||||
|
||||
evaluationKey[i] = make([][2]*ring.Poly, ekg.bitLog)
|
||||
|
||||
for w := uint64(0); w < ekg.bitLog; w++ {
|
||||
|
||||
evaluationKey[i][w][0] = h[0][i][w][0].CopyNew()
|
||||
evaluationKey[i][w][1] = h[0][i][w][1].CopyNew()
|
||||
|
||||
for j := 1; j < len(h); j++ {
|
||||
ekg.context.AddNoMod(evaluationKey[i][w][0], h[j][i][w][0], evaluationKey[i][w][0])
|
||||
ekg.context.AddNoMod(evaluationKey[i][w][1], h[j][i][w][1], evaluationKey[i][w][1])
|
||||
|
||||
if j&7 == 7 {
|
||||
ekg.context.Reduce(evaluationKey[i][w][0], evaluationKey[i][w][0])
|
||||
ekg.context.Reduce(evaluationKey[i][w][1], evaluationKey[i][w][1])
|
||||
}
|
||||
}
|
||||
|
||||
if (len(h)-1)&7 == 7 {
|
||||
ekg.context.Reduce(evaluationKey[i][w][0], evaluationKey[i][w][0])
|
||||
ekg.context.Reduce(evaluationKey[i][w][1], evaluationKey[i][w][1])
|
||||
}
|
||||
|
||||
ekg.context.MForm(evaluationKey[i][w][0], evaluationKey[i][w][0])
|
||||
ekg.context.MForm(evaluationKey[i][w][1], evaluationKey[i][w][1])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
53
dbfv/collective_KG.go
Normal file
53
dbfv/collective_KG.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package dbfv
|
||||
|
||||
import (
|
||||
"github.com/lca1/lattigo/bfv"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
)
|
||||
|
||||
type CKG struct {
|
||||
context *ring.Context
|
||||
gaussianSampler *ring.KYSampler
|
||||
|
||||
share *ring.Poly
|
||||
cpk [2]*ring.Poly
|
||||
}
|
||||
|
||||
func NewCKG(context *ring.Context, crs *ring.Poly) *CKG {
|
||||
ckg := new(CKG)
|
||||
ckg.context = context
|
||||
ckg.gaussianSampler = context.NewKYSampler(3.19, 19)
|
||||
|
||||
ckg.cpk[0] = context.NewPoly()
|
||||
ckg.cpk[1] = crs.CopyNew()
|
||||
|
||||
return ckg
|
||||
}
|
||||
|
||||
func (ckg *CKG) GenShare(sk *ring.Poly) error {
|
||||
|
||||
// -(sk * crs) + e
|
||||
ckg.share = ckg.gaussianSampler.SampleNTTNew()
|
||||
ckg.context.MulCoeffsMontgomeryAndSub(sk, ckg.cpk[1], ckg.share)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ckg *CKG) GetShare() *ring.Poly {
|
||||
return ckg.share
|
||||
}
|
||||
|
||||
func (ckg *CKG) AggregateShares(shares []*ring.Poly) error {
|
||||
|
||||
for i := 0; i < len(shares); i++ {
|
||||
ckg.context.Add(ckg.cpk[0], shares[i], ckg.cpk[0])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ckg *CKG) Finalize() (*bfv.PublicKey, error) {
|
||||
collectivePk := new(bfv.PublicKey)
|
||||
collectivePk.Set(ckg.cpk)
|
||||
return collectivePk, nil
|
||||
}
|
||||
55
dbfv/collective_KS.go
Normal file
55
dbfv/collective_KS.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package dbfv
|
||||
|
||||
import (
|
||||
"github.com/lca1/lattigo/ring"
|
||||
//"fmt"
|
||||
)
|
||||
|
||||
type CKS struct {
|
||||
context *ring.Context
|
||||
|
||||
sigmaSmudging float64
|
||||
gaussianSampler *ring.KYSampler
|
||||
|
||||
deltaSk *ring.Poly
|
||||
}
|
||||
|
||||
func NewCKS(skInput, skOutput *ring.Poly, context *ring.Context, sigmaSmudging float64) *CKS {
|
||||
|
||||
cks := new(CKS)
|
||||
cks.context = context
|
||||
|
||||
cks.sigmaSmudging = sigmaSmudging
|
||||
cks.gaussianSampler = context.NewKYSampler(sigmaSmudging, int(6*sigmaSmudging))
|
||||
|
||||
cks.deltaSk = cks.context.NewPoly()
|
||||
context.Sub(skInput, skOutput, cks.deltaSk)
|
||||
return cks
|
||||
}
|
||||
|
||||
func (cks *CKS) KeySwitch(c1 *ring.Poly) *ring.Poly {
|
||||
|
||||
h := c1.CopyNew()
|
||||
|
||||
cks.context.NTT(h, h)
|
||||
cks.context.MulCoeffsMontgomery(h, cks.deltaSk, h)
|
||||
cks.context.InvNTT(h, h)
|
||||
cks.context.Add(h, cks.gaussianSampler.SampleNew(), h)
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (cks *CKS) Aggregate(c0 *ring.Poly, h []*ring.Poly) {
|
||||
|
||||
for i := range h {
|
||||
cks.context.AddNoMod(c0, h[i], c0)
|
||||
|
||||
if i&7 == 1 {
|
||||
cks.context.Reduce(c0, c0)
|
||||
}
|
||||
}
|
||||
|
||||
if len(h)&7 != 7 {
|
||||
cks.context.Reduce(c0, c0)
|
||||
}
|
||||
}
|
||||
93
dbfv/collective_PKS.go
Normal file
93
dbfv/collective_PKS.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package dbfv
|
||||
|
||||
import (
|
||||
"github.com/lca1/lattigo/ring"
|
||||
//"fmt"
|
||||
)
|
||||
|
||||
type PCKS struct {
|
||||
context *ring.Context
|
||||
|
||||
sigmaSmudging float64
|
||||
gaussianSamplerSmudge *ring.KYSampler
|
||||
gaussianSampler *ring.KYSampler
|
||||
ternarySampler *ring.TernarySampler
|
||||
|
||||
skInput *ring.Poly
|
||||
pkOutput [2]*ring.Poly
|
||||
|
||||
polypool *ring.Poly
|
||||
}
|
||||
|
||||
func NewPCKS(skInput *ring.Poly, pkOutput [2]*ring.Poly, context *ring.Context, sigmaSmudging float64) *PCKS {
|
||||
|
||||
pcks := new(PCKS)
|
||||
pcks.context = context
|
||||
pcks.sigmaSmudging = sigmaSmudging
|
||||
|
||||
pcks.gaussianSamplerSmudge = context.NewKYSampler(sigmaSmudging, int(6*sigmaSmudging))
|
||||
pcks.gaussianSampler = context.NewKYSampler(3.19, 19)
|
||||
pcks.ternarySampler = context.NewTernarySampler()
|
||||
|
||||
pcks.skInput = skInput
|
||||
pcks.pkOutput = pkOutput
|
||||
pcks.polypool = context.NewPoly()
|
||||
return pcks
|
||||
}
|
||||
|
||||
func (pcks *PCKS) KeySwitch(ct1 *ring.Poly) (h [2]*ring.Poly) {
|
||||
|
||||
// h_0 = pk_0 (NTT)
|
||||
h[0] = pcks.pkOutput[0].CopyNew()
|
||||
// h_1 = pk_1 (NTT)
|
||||
h[1] = pcks.pkOutput[1].CopyNew()
|
||||
|
||||
//u_i
|
||||
pcks.ternarySampler.SampleMontgomeryNTT(pcks.polypool)
|
||||
|
||||
// h_0 = u_i * pk_0 (NTT)
|
||||
pcks.context.MulCoeffsMontgomery(h[0], pcks.polypool, h[0])
|
||||
// h_1 = u_i * pk_1 (NTT)
|
||||
pcks.context.MulCoeffsMontgomery(h[1], pcks.polypool, h[1])
|
||||
|
||||
// h0 = u_i * pk_0 + s_i*c_1 (NTT)
|
||||
pcks.context.NTT(ct1, pcks.polypool)
|
||||
pcks.context.MulCoeffsMontgomeryAndAdd(pcks.polypool, pcks.skInput, h[0])
|
||||
|
||||
pcks.context.InvNTT(h[0], h[0])
|
||||
pcks.context.InvNTT(h[1], h[1])
|
||||
|
||||
// h_0 = InvNTT(s_i*c_1 + u_i * pk_0) + e0
|
||||
pcks.gaussianSamplerSmudge.Sample(pcks.polypool)
|
||||
pcks.context.Add(h[0], pcks.polypool, h[0])
|
||||
|
||||
// h_1 = InvNTT(u_i * pk_1) + e1
|
||||
pcks.gaussianSampler.Sample(pcks.polypool)
|
||||
pcks.context.Add(h[1], pcks.polypool, h[1])
|
||||
|
||||
pcks.polypool.Zero()
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (pcks *PCKS) Aggregate(ct []*ring.Poly, h [][2]*ring.Poly) {
|
||||
|
||||
pcks.context.AddNoMod(ct[0], h[0][0], ct[0])
|
||||
ct[1] = h[0][1].CopyNew()
|
||||
|
||||
for i := 1; i < len(h); i++ {
|
||||
pcks.context.AddNoMod(ct[0], h[i][0], ct[0])
|
||||
pcks.context.AddNoMod(ct[1], h[i][1], ct[1])
|
||||
|
||||
if i&7 == 7 {
|
||||
pcks.context.Reduce(ct[0], ct[0])
|
||||
pcks.context.Reduce(ct[1], ct[1])
|
||||
}
|
||||
}
|
||||
|
||||
if len(h)&7 != 7 {
|
||||
pcks.context.Reduce(ct[0], ct[0])
|
||||
pcks.context.Reduce(ct[1], ct[1])
|
||||
}
|
||||
|
||||
}
|
||||
234
dbfv/dbfv_benchmark_test.go
Normal file
234
dbfv/dbfv_benchmark_test.go
Normal file
@@ -0,0 +1,234 @@
|
||||
package dbfv
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/lca1/lattigo/bfv"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
"log"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Benchmark_DBFVScheme(b *testing.B) {
|
||||
|
||||
paramSets := bfv.ParamSets60[3:4]
|
||||
bitDecomps := []uint64{60}
|
||||
nParties := []int{2}
|
||||
|
||||
for _, params := range paramSets {
|
||||
|
||||
bfvContext := bfv.NewBfvContext()
|
||||
if err := bfvContext.SetParameters(params.N, params.T, params.Qi, params.Pi, params.Sigma); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
context := bfvContext.GetContextQ()
|
||||
|
||||
kgen := bfvContext.NewKeyGenerator()
|
||||
|
||||
sk0, pk0, err := kgen.NewKeyPair()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
sk1, pk1, err := kgen.NewKeyPair()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
crpGenerator, err := NewCRPGenerator(nil, context)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
crpGenerator.Seed([]byte{})
|
||||
|
||||
// EKG_Naive
|
||||
for _, parties := range nParties {
|
||||
|
||||
for _, bitDecomp := range bitDecomps {
|
||||
|
||||
ekgV2Naive := NewEkgProtocolNaive(context, bitDecomp)
|
||||
|
||||
// [nParties][CrtDecomp][WDecomp][2]
|
||||
samples := make([][][][2]*ring.Poly, parties)
|
||||
for i := 0; i < parties; i++ {
|
||||
samples[i] = ekgV2Naive.GenSamples(sk0.Get(), pk0.Get())
|
||||
}
|
||||
|
||||
aggregatedSamples := make([][][][2]*ring.Poly, parties)
|
||||
for i := 0; i < parties; i++ {
|
||||
aggregatedSamples[i] = ekgV2Naive.Aggregate(sk0.Get(), pk0.Get(), samples)
|
||||
}
|
||||
|
||||
//EKG_V2_Naive_Round_0
|
||||
b.Run(fmt.Sprintf("params=%d/parties=%d/decomp=%d/EKG_Naive_Round0", params.N, parties, bitDecomp), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ekgV2Naive.GenSamples(sk0.Get(), pk1.Get())
|
||||
}
|
||||
})
|
||||
|
||||
//EKG_V2_Naive_Round_1
|
||||
b.Run(fmt.Sprintf("params=%d/parties=%d/decomp=%d/EKG_Naive_Round1", params.N, parties, bitDecomp), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ekgV2Naive.Aggregate(sk0.Get(), pk1.Get(), samples)
|
||||
}
|
||||
})
|
||||
|
||||
//EKG_V2_Naive_Round_2
|
||||
b.Run(fmt.Sprintf("params=%d/parties=%d/decomp=%d/EKG_Naive_Round2", params.N, parties, bitDecomp), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ekgV2Naive.Finalize(aggregatedSamples)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// EKG
|
||||
for _, parties := range nParties {
|
||||
|
||||
for _, bitDecomp := range bitDecomps {
|
||||
|
||||
bitLog := uint64((60 + (60 % bitDecomp)) / bitDecomp)
|
||||
|
||||
EkgProtocol := NewEkgProtocol(context, bitDecomp)
|
||||
|
||||
crp := make([][]*ring.Poly, len(context.Modulus))
|
||||
|
||||
for i := 0; i < len(context.Modulus); i++ {
|
||||
crp[i] = make([]*ring.Poly, bitLog)
|
||||
for j := uint64(0); j < bitLog; j++ {
|
||||
crp[i][j] = crpGenerator.Clock()
|
||||
}
|
||||
}
|
||||
|
||||
samples := make([][][]*ring.Poly, parties)
|
||||
for i := 0; i < parties; i++ {
|
||||
samples[i] = make([][]*ring.Poly, len(context.Modulus))
|
||||
samples[i] = EkgProtocol.GenSamples(sk0.Get(), sk1.Get(), crp)
|
||||
}
|
||||
|
||||
aggregatedSamples := make([][][][2]*ring.Poly, parties)
|
||||
for i := 0; i < parties; i++ {
|
||||
aggregatedSamples[i] = EkgProtocol.Aggregate(sk1.Get(), samples, crp)
|
||||
}
|
||||
|
||||
keySwitched := make([][][]*ring.Poly, parties)
|
||||
|
||||
sum := EkgProtocol.Sum(aggregatedSamples)
|
||||
for i := 0; i < parties; i++ {
|
||||
keySwitched[i] = EkgProtocol.KeySwitch(sk0.Get(), sk1.Get(), sum)
|
||||
}
|
||||
|
||||
//EKG_V2_Round_0
|
||||
b.Run(fmt.Sprintf("params=%d/parties=%d/decomp=%d/EKG_Round0", params.N, parties, bitDecomp), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
EkgProtocol.GenSamples(sk0.Get(), sk1.Get(), crp)
|
||||
}
|
||||
})
|
||||
|
||||
//EKG_V2_Round_1
|
||||
b.Run(fmt.Sprintf("params=%d/parties=%d/decomp=%d/EKG_Round1", params.N, parties, bitDecomp), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
EkgProtocol.Aggregate(sk1.Get(), samples, crp)
|
||||
}
|
||||
})
|
||||
|
||||
//EKG_V2_Round_2
|
||||
b.Run(fmt.Sprintf("params=%d/parties=%d/decomp=%d/EKG_Round2", params.N, parties, bitDecomp), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
EkgProtocol.KeySwitch(sk1.Get(), sk1.Get(), EkgProtocol.Sum(aggregatedSamples))
|
||||
}
|
||||
})
|
||||
|
||||
//EKG_V2_Round_3
|
||||
b.Run(fmt.Sprintf("params=%d/parties=%d/decomp=%d/EKG_Round3", params.N, parties, bitDecomp), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
EkgProtocol.ComputeEVK(keySwitched, sum)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//CKG
|
||||
for _, parties := range nParties {
|
||||
|
||||
ckgInstance := NewCKG(context, crpGenerator.Clock())
|
||||
ckgInstance.GenShare(sk0.Get())
|
||||
|
||||
shares := make([]*ring.Poly, parties)
|
||||
for i := 0; i < parties; i++ {
|
||||
shares[i] = ckgInstance.GetShare()
|
||||
}
|
||||
|
||||
// CKG_Round_0
|
||||
b.Run(fmt.Sprintf("params=%d/parties=%d/CKG_Round0", params.N, parties), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ckgInstance.GenShare(sk0.Get())
|
||||
}
|
||||
})
|
||||
|
||||
// CKG_Round_1
|
||||
b.Run(fmt.Sprintf("params=%d/parties=%d/CKG_Round1", params.N, parties), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ckgInstance.AggregateShares(shares)
|
||||
//ckgInstance.Finalize()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//CKS
|
||||
sigmaSmudging := 3.19
|
||||
for _, parties := range nParties {
|
||||
|
||||
cksInstance := NewCKS(sk0.Get(), sk1.Get(), context, sigmaSmudging)
|
||||
|
||||
ciphertext := bfvContext.NewRandomCiphertext(1)
|
||||
|
||||
hi := make([]*ring.Poly, parties)
|
||||
for i := 0; i < parties; i++ {
|
||||
hi[i] = cksInstance.KeySwitch(ciphertext.Value()[1])
|
||||
}
|
||||
|
||||
// CKS_Round_0
|
||||
b.Run(fmt.Sprintf("params=%d/parties=%d/sigmaSmudging=%f/CKS_Round0", params.N, parties, sigmaSmudging), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
cksInstance.KeySwitch(ciphertext.Value()[1])
|
||||
}
|
||||
})
|
||||
|
||||
// CKS_Round_1
|
||||
b.Run(fmt.Sprintf("params=%d/parties=%d/sigmaSmudging=%f/CKS_Round1", params.N, parties, sigmaSmudging), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
cksInstance.Aggregate(ciphertext.Value()[0], hi)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//CKS_Trustless
|
||||
for _, parties := range nParties {
|
||||
|
||||
pcks := NewPCKS(sk0.Get(), pk1.Get(), context, sigmaSmudging)
|
||||
|
||||
ciphertext := bfvContext.NewRandomCiphertext(1)
|
||||
|
||||
hi := make([][2]*ring.Poly, parties)
|
||||
for i := 0; i < parties; i++ {
|
||||
hi[i] = pcks.KeySwitch(ciphertext.Value()[1])
|
||||
}
|
||||
|
||||
// CKS_Trustless_Round_0
|
||||
b.Run(fmt.Sprintf("params=%d/parties=%d/sigmaSmudging=%f/PCKS_Round0", params.N, parties, sigmaSmudging), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
pcks.KeySwitch(ciphertext.Value()[1])
|
||||
}
|
||||
})
|
||||
|
||||
// CKS_Trustless_Round_1
|
||||
b.Run(fmt.Sprintf("params=%d/parties=%d/sigmaSmudging=%f/PCKS_Round1", params.N, parties, sigmaSmudging), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
pcks.Aggregate(ciphertext.Value(), hi)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
470
dbfv/dbfv_test.go
Normal file
470
dbfv/dbfv_test.go
Normal file
@@ -0,0 +1,470 @@
|
||||
package dbfv
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/lca1/lattigo/bfv"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
"log"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_DBFVScheme(t *testing.T) {
|
||||
|
||||
paramSets := bfv.ParamSets60[0:1]
|
||||
bitDecomps := []uint64{60}
|
||||
nParties := []int{5}
|
||||
|
||||
//sigmaSmudging := 6.36
|
||||
|
||||
for _, params := range paramSets {
|
||||
|
||||
// nParties data indpendant element
|
||||
bfvContext := bfv.NewBfvContext()
|
||||
if err := bfvContext.SetParameters(params.N, params.T, params.Qi, params.Pi, params.Sigma); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
kgen := bfvContext.NewKeyGenerator()
|
||||
|
||||
evaluator, err := bfvContext.NewEvaluator()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
context := bfvContext.GetContextQ()
|
||||
|
||||
contextT := bfvContext.GetContextT()
|
||||
|
||||
encoder := bfvContext.NewBatchEncoder()
|
||||
|
||||
coeffsWant := contextT.NewUniformPoly()
|
||||
plaintextWant := bfvContext.NewPlaintext()
|
||||
encoder.EncodeUint(coeffsWant.Coeffs[0], plaintextWant)
|
||||
|
||||
ciphertextTest := bfvContext.NewCiphertext(1)
|
||||
|
||||
for _, parties := range nParties {
|
||||
|
||||
crpGenerators := make([]*CRPGenerator, parties)
|
||||
for i := 0; i < parties; i++ {
|
||||
crpGenerators[i], err = NewCRPGenerator(nil, context)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
crpGenerators[i].Seed([]byte{})
|
||||
}
|
||||
|
||||
// SecretKeys
|
||||
sk0_shards := make([]*bfv.SecretKey, parties)
|
||||
sk1_shards := make([]*bfv.SecretKey, parties)
|
||||
tmp0 := context.NewPoly()
|
||||
tmp1 := context.NewPoly()
|
||||
|
||||
for i := 0; i < parties; i++ {
|
||||
sk0_shards[i] = kgen.NewSecretKey()
|
||||
sk1_shards[i] = kgen.NewSecretKey()
|
||||
context.Add(tmp0, sk0_shards[i].Get(), tmp0)
|
||||
context.Add(tmp1, sk1_shards[i].Get(), tmp1)
|
||||
}
|
||||
|
||||
sk0 := new(bfv.SecretKey)
|
||||
sk1 := new(bfv.SecretKey)
|
||||
|
||||
sk0.Set(tmp0)
|
||||
sk1.Set(tmp1)
|
||||
|
||||
// Publickeys
|
||||
pk0, err := kgen.NewPublicKey(sk0)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
pk1, err := kgen.NewPublicKey(sk1)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Encryptors
|
||||
encryptor_pk0, err := bfvContext.NewEncryptor(pk0)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
//encryptor_pk1, err := bfvContext.NewEncryptor(pk1)
|
||||
//if err != nil {
|
||||
// log.Fatal(err)
|
||||
//}
|
||||
|
||||
// Decryptors
|
||||
decryptor_sk0, err := bfvContext.NewDecryptor(sk0, 1)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
decryptor_sk1, err := bfvContext.NewDecryptor(sk1, 1)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Reference ciphertext
|
||||
ciphertext, err := encryptor_pk0.EncryptNew(plaintextWant)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
coeffsMul := contextT.NewPoly()
|
||||
for i := 0; i < 1; i++ {
|
||||
ciphertext = evaluator.MulNew(ciphertext, ciphertext).(*bfv.Ciphertext)
|
||||
contextT.MulCoeffs(coeffsWant, coeffsWant, coeffsMul)
|
||||
}
|
||||
|
||||
t.Run(fmt.Sprintf("N=%d/Qi=%dx%d/CRS_PRNG", context.N, len(context.Modulus), 60), func(t *testing.T) {
|
||||
|
||||
Ha, _ := NewPRNG([]byte{})
|
||||
Hb, _ := NewPRNG([]byte{})
|
||||
|
||||
// Random 32 byte seed
|
||||
seed1 := []byte{0x48, 0xc3, 0x31, 0x12, 0x74, 0x98, 0xd3, 0xf2,
|
||||
0x7b, 0x15, 0x15, 0x9b, 0x50, 0xc4, 0x9c, 0x00,
|
||||
0x7d, 0xa5, 0xea, 0x68, 0x1f, 0xed, 0x4f, 0x99,
|
||||
0x54, 0xc0, 0x52, 0xc0, 0x75, 0xff, 0xf7, 0x5c}
|
||||
|
||||
// New reseed of the PRNG after one clock cycle with the seed1
|
||||
seed2 := []byte{250, 228, 6, 63, 97, 110, 68, 153,
|
||||
147, 236, 236, 37, 152, 89, 129, 32,
|
||||
185, 5, 221, 180, 160, 217, 247, 201,
|
||||
211, 188, 160, 163, 176, 83, 83, 138}
|
||||
|
||||
Ha.Seed(seed1)
|
||||
Hb.Seed(append(seed1, seed2...)) //Append works since blake2b hashes blocks of 512 bytes
|
||||
|
||||
Ha.SetClock(256)
|
||||
Hb.SetClock(255)
|
||||
|
||||
a := Ha.Clock()
|
||||
b := Hb.Clock()
|
||||
|
||||
for i := 0; i < 32; i++ {
|
||||
if a[i] != b[i] {
|
||||
t.Errorf("error : error prng")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
crs_generator_1, _ := NewCRPGenerator(nil, context)
|
||||
crs_generator_2, _ := NewCRPGenerator(nil, context)
|
||||
|
||||
crs_generator_1.Seed(seed1)
|
||||
crs_generator_2.Seed(append(seed1, seed2...)) //Append works since blake2b hashes blocks of 512 bytes
|
||||
|
||||
crs_generator_1.SetClock(256)
|
||||
crs_generator_2.SetClock(255)
|
||||
|
||||
p0 := crs_generator_1.Clock()
|
||||
p1 := crs_generator_2.Clock()
|
||||
|
||||
if bfvContext.GetContextQ().Equal(p0, p1) != true {
|
||||
t.Errorf("error : crs prng generator")
|
||||
}
|
||||
})
|
||||
|
||||
// EKG_Naive
|
||||
for _, bitDecomp := range bitDecomps {
|
||||
|
||||
t.Run(fmt.Sprintf("N=%d/Qi=%dx%d/bitdecomp=%d/EKG", context.N, len(context.Modulus), 60, bitDecomp), func(t *testing.T) {
|
||||
|
||||
bitLog := uint64((60 + (60 % bitDecomp)) / bitDecomp)
|
||||
|
||||
// Each party instantiate an ekg naive protocole
|
||||
ekg := make([]*EkgProtocol, parties)
|
||||
ephemeralKeys := make([]*ring.Poly, parties)
|
||||
crp := make([][][]*ring.Poly, parties)
|
||||
|
||||
for i := 0; i < parties; i++ {
|
||||
|
||||
ekg[i] = NewEkgProtocol(context, bitDecomp)
|
||||
ephemeralKeys[i] = ekg[i].NewEphemeralKey()
|
||||
crp[i] = make([][]*ring.Poly, len(context.Modulus))
|
||||
|
||||
for j := 0; j < len(context.Modulus); j++ {
|
||||
crp[i][j] = make([]*ring.Poly, bitLog)
|
||||
for u := uint64(0); u < bitLog; u++ {
|
||||
crp[i][j][u] = crpGenerators[i].Clock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
evk := test_EKG_Protocol(parties, ekg, sk0_shards, ephemeralKeys, crp)
|
||||
|
||||
rlk, err := kgen.SetRelinKeys([][][][2]*ring.Poly{evk[0]}, bitDecomp)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := evaluator.Relinearize(ciphertext, rlk, ciphertextTest); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
plaintextTest, err := decryptor_sk0.DecryptNew(ciphertextTest)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
coeffsTest, err := encoder.DecodeUint(plaintextTest)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if bfv.EqualSlice(coeffsMul.Coeffs[0], coeffsTest) != true {
|
||||
t.Errorf("error : ekg rlk bad decrypt")
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
// EKG_Naive
|
||||
for _, bitDecomp := range bitDecomps {
|
||||
|
||||
t.Run(fmt.Sprintf("N=%d/Qi=%dx%d/bitdecomp=%d/EKG_Naive", context.N, len(context.Modulus), 60, bitDecomp), func(t *testing.T) {
|
||||
|
||||
// Each party instantiate an ekg naive protocole
|
||||
ekgNaive := make([]*EkgProtocolNaive, parties)
|
||||
for i := 0; i < parties; i++ {
|
||||
ekgNaive[i] = NewEkgProtocolNaive(context, bitDecomp)
|
||||
}
|
||||
|
||||
evk := test_EKG_Protocol_Naive(parties, sk0_shards, pk0, ekgNaive)
|
||||
|
||||
rlk, err := kgen.SetRelinKeys([][][][2]*ring.Poly{evk[0]}, bitDecomp)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := evaluator.Relinearize(ciphertext, rlk, ciphertextTest); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
plaintextTest, err := decryptor_sk0.DecryptNew(ciphertextTest)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
coeffsTest, err := encoder.DecodeUint(plaintextTest)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if bfv.EqualSlice(coeffsMul.Coeffs[0], coeffsTest) != true {
|
||||
t.Errorf("error : ekg_naive rlk bad decrypt")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
t.Run(fmt.Sprintf("N=%d/Qi=%dx%d/CKG", context.N, len(context.Modulus), 60), func(t *testing.T) {
|
||||
|
||||
crp := make([]*ring.Poly, parties)
|
||||
for i := 0; i < parties; i++ {
|
||||
crp[i] = crpGenerators[i].Clock()
|
||||
}
|
||||
|
||||
ckg := make([]*CKG, parties)
|
||||
for i := 0; i < parties; i++ {
|
||||
ckg[i] = NewCKG(context, crp[i])
|
||||
}
|
||||
|
||||
// Each party creates a new CKG instance
|
||||
shares := make([]*ring.Poly, parties)
|
||||
for i := 0; i < parties; i++ {
|
||||
ckg[i].GenShare(sk0_shards[i].Get())
|
||||
shares[i] = ckg[i].GetShare()
|
||||
}
|
||||
|
||||
pkTest := make([]*bfv.PublicKey, parties)
|
||||
for i := 0; i < parties; i++ {
|
||||
ckg[i].AggregateShares(shares)
|
||||
pkTest[i], err = ckg[i].Finalize()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Verifies that all parties have the same share collective public key
|
||||
for i := 1; i < parties; i++ {
|
||||
if context.Equal(pkTest[0].Get()[0], pkTest[i].Get()[0]) != true || bfvContext.GetContextQ().Equal(pkTest[0].Get()[1], pkTest[i].Get()[1]) != true {
|
||||
t.Errorf("error : ckg protocol, cpk establishement")
|
||||
}
|
||||
}
|
||||
|
||||
// Verifies that decrypt((encryptp(collectiveSk, m), collectivePk) = m
|
||||
encryptorTest, err := bfvContext.NewEncryptor(pkTest[0])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
ciphertextTest, err := encryptorTest.EncryptNew(plaintextWant)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
plaintextTest, err := decryptor_sk0.DecryptNew(ciphertextTest)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
coeffsTest, err := encoder.DecodeUint(plaintextTest)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if bfv.EqualSlice(coeffsWant.Coeffs[0], coeffsTest) != true {
|
||||
t.Errorf("error : ckg protocol, cpk encrypt/decrypt test")
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("N=%d/Qi=%dx%d/CKS", context.N, len(context.Modulus), 60), func(t *testing.T) {
|
||||
|
||||
ciphertext, err := encryptor_pk0.EncryptNew(plaintextWant)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
ciphertexts := make([]*bfv.Ciphertext, parties)
|
||||
for i := 0; i < parties; i++ {
|
||||
ciphertexts[i] = ciphertext.CopyNew().(*bfv.Ciphertext)
|
||||
}
|
||||
|
||||
// Each party creates its CKS instance with deltaSk = si-si'
|
||||
cks := make([]*CKS, parties)
|
||||
for i := 0; i < parties; i++ {
|
||||
cks[i] = NewCKS(sk0_shards[i].Get(), sk1_shards[i].Get(), context, 6.36)
|
||||
}
|
||||
|
||||
// Each party computes its hi share from the shared ciphertext
|
||||
// Each party encodes its share and sends it to the other n-1 parties
|
||||
hi := make([]*ring.Poly, parties)
|
||||
for i := 0; i < parties; i++ {
|
||||
hi[i] = cks[i].KeySwitch(ciphertexts[i].Value()[1])
|
||||
}
|
||||
// Each party receive the shares n-1 shares from the other parties and decodes them
|
||||
for i := 0; i < parties; i++ {
|
||||
// Then keyswitch the ciphertext with the decoded shares
|
||||
cks[i].Aggregate(ciphertexts[i].Value()[0], hi)
|
||||
}
|
||||
|
||||
for i := 0; i < parties; i++ {
|
||||
|
||||
plaintextHave, _ := decryptor_sk1.DecryptNew(ciphertexts[i])
|
||||
|
||||
coeffsTest, err := encoder.DecodeUint(plaintextHave)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if bfv.EqualSlice(coeffsWant.Coeffs[0], coeffsTest) != true {
|
||||
t.Errorf("error : CKS")
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("N=%d/Qi=%dx%d/PCKS", context.N, len(context.Modulus), 60), func(t *testing.T) {
|
||||
|
||||
ciphertext, err := encryptor_pk0.EncryptNew(plaintextWant)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
ciphertexts := make([]*bfv.Ciphertext, parties)
|
||||
for i := 0; i < parties; i++ {
|
||||
ciphertexts[i] = ciphertext.CopyNew().(*bfv.Ciphertext)
|
||||
}
|
||||
|
||||
pcks := make([]*PCKS, parties)
|
||||
for i := 0; i < parties; i++ {
|
||||
pcks[i] = NewPCKS(sk0_shards[i].Get(), pk1.Get(), context, 6.36)
|
||||
}
|
||||
|
||||
hi := make([][2]*ring.Poly, parties)
|
||||
for i := 0; i < parties; i++ {
|
||||
hi[i] = pcks[i].KeySwitch(ciphertexts[i].Value()[1])
|
||||
}
|
||||
|
||||
for i := 0; i < parties; i++ {
|
||||
pcks[i].Aggregate(ciphertexts[i].Value(), hi)
|
||||
}
|
||||
|
||||
for i := 0; i < parties; i++ {
|
||||
plaintextHave, _ := decryptor_sk1.DecryptNew(ciphertexts[i])
|
||||
|
||||
coeffsTest, err := encoder.DecodeUint(plaintextHave)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if bfv.EqualSlice(coeffsWant.Coeffs[0], coeffsTest) != true {
|
||||
t.Errorf("error : PCKS")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func test_EKG_Protocol_Naive(parties int, sk []*bfv.SecretKey, collectivePk *bfv.PublicKey, ekgNaive []*EkgProtocolNaive) [][][][2]*ring.Poly {
|
||||
|
||||
// ROUND 0
|
||||
// Each party generates its samples
|
||||
samples := make([][][][2]*ring.Poly, parties)
|
||||
for i := 0; i < parties; i++ {
|
||||
samples[i] = ekgNaive[i].GenSamples(sk[i].Get(), collectivePk.Get())
|
||||
}
|
||||
|
||||
// ROUND 1
|
||||
// Each party aggretates its sample with the other n-1 samples
|
||||
aggregatedSamples := make([][][][2]*ring.Poly, parties)
|
||||
for i := 0; i < parties; i++ {
|
||||
aggregatedSamples[i] = ekgNaive[i].Aggregate(sk[i].Get(), collectivePk.Get(), samples)
|
||||
}
|
||||
|
||||
// ROUND 2
|
||||
// Each party aggregates sums its aggregatedSample with the other n-1 aggregated samples
|
||||
evk := make([][][][2]*ring.Poly, parties)
|
||||
for i := 0; i < parties; i++ {
|
||||
evk[i] = ekgNaive[i].Finalize(aggregatedSamples)
|
||||
}
|
||||
|
||||
return evk
|
||||
}
|
||||
|
||||
func test_EKG_Protocol(parties int, ekgProtocols []*EkgProtocol, sk []*bfv.SecretKey, ephemeralKeys []*ring.Poly, crp [][][]*ring.Poly) [][][][2]*ring.Poly {
|
||||
|
||||
// ROUND 1
|
||||
samples := make([][][]*ring.Poly, parties)
|
||||
for i := 0; i < parties; i++ {
|
||||
samples[i] = ekgProtocols[i].GenSamples(ephemeralKeys[i], sk[i].Get(), crp[i])
|
||||
}
|
||||
|
||||
//ROUND 2
|
||||
aggregatedSamples := make([][][][2]*ring.Poly, parties)
|
||||
for i := 0; i < parties; i++ {
|
||||
aggregatedSamples[i] = ekgProtocols[i].Aggregate(sk[i].Get(), samples, crp[i])
|
||||
}
|
||||
|
||||
// ROUND 3
|
||||
keySwitched := make([][][]*ring.Poly, parties)
|
||||
sum := make([][][][2]*ring.Poly, parties)
|
||||
for i := 0; i < parties; i++ {
|
||||
sum[i] = ekgProtocols[i].Sum(aggregatedSamples)
|
||||
keySwitched[i] = ekgProtocols[i].KeySwitch(ephemeralKeys[i], sk[i].Get(), sum[i])
|
||||
}
|
||||
|
||||
// ROUND 4
|
||||
collectiveEvaluationKey := make([][][][2]*ring.Poly, parties)
|
||||
for i := 0; i < parties; i++ {
|
||||
collectiveEvaluationKey[i] = ekgProtocols[i].ComputeEVK(keySwitched, sum[i])
|
||||
}
|
||||
|
||||
return collectiveEvaluationKey
|
||||
}
|
||||
148
dckks/collective_CRS.go
Normal file
148
dckks/collective_CRS.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package dckks
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
"golang.org/x/crypto/blake2b"
|
||||
"hash"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
type PRNG struct {
|
||||
clock uint64
|
||||
seed []byte
|
||||
hash hash.Hash
|
||||
}
|
||||
|
||||
// NewPRNG creates a new instance of PRNG
|
||||
// Accepts an optional key, if no key, call with key=nill
|
||||
func NewPRNG(key []byte) (*PRNG, error) {
|
||||
var err error
|
||||
prng := new(PRNG)
|
||||
prng.clock = 0
|
||||
prng.hash, err = blake2b.New512(key)
|
||||
return prng, err
|
||||
}
|
||||
|
||||
// GetClock returns the value of the clock cycle of the PRNG
|
||||
func (prng *PRNG) GetClock() uint64 {
|
||||
return prng.clock
|
||||
}
|
||||
|
||||
// Seed resets the current state of the PRNG (without changing the
|
||||
// optional key) and seeds it with the given bytes.
|
||||
// Seed will also reset the clock cycle to 0.
|
||||
func (prng *PRNG) Seed(seed []byte) {
|
||||
prng.hash.Reset()
|
||||
prng.seed = seed[:]
|
||||
prng.hash.Write(seed)
|
||||
prng.clock = 0
|
||||
}
|
||||
|
||||
func (prng *PRNG) GetSeed() []byte {
|
||||
return prng.seed[:]
|
||||
}
|
||||
|
||||
// Clock returns the right part of the digest value
|
||||
// of the current PRNG state and reseeds the PRNG with the
|
||||
// left part of the digest value. Increases the clock cycle by 1.
|
||||
func (prng *PRNG) Clock() []byte {
|
||||
tmp := prng.hash.Sum(nil)
|
||||
prng.hash.Write(tmp[:32])
|
||||
prng.clock += 1
|
||||
return tmp[32:]
|
||||
}
|
||||
|
||||
// Sets the clock cycle of the PRNG to a given number. Returns an error if
|
||||
// the target clock cycle is smaller than the current clock cycle.
|
||||
func (prng *PRNG) SetClock(n uint64) error {
|
||||
if prng.clock > n {
|
||||
return errors.New("error : cannot set prng clock to a previous state")
|
||||
}
|
||||
var tmp []byte
|
||||
for prng.clock != n {
|
||||
tmp = prng.hash.Sum(nil)
|
||||
prng.hash.Write(tmp[:32])
|
||||
prng.clock += 1
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Common Reference Polynomial Generator
|
||||
type CRPGenerator struct {
|
||||
prng *PRNG
|
||||
context *ring.Context
|
||||
masks []uint64
|
||||
}
|
||||
|
||||
func NewCRPGenerator(key []byte, context *ring.Context) (*CRPGenerator, error) {
|
||||
var err error
|
||||
crpgenerator := new(CRPGenerator)
|
||||
crpgenerator.prng, err = NewPRNG(key)
|
||||
crpgenerator.context = context
|
||||
crpgenerator.masks = make([]uint64, len(context.Modulus))
|
||||
for i, qi := range context.Modulus {
|
||||
crpgenerator.masks[i] = (1 << uint64(bits.Len64(qi))) - 1
|
||||
}
|
||||
return crpgenerator, err
|
||||
}
|
||||
|
||||
func (crpgenerator *CRPGenerator) GetClock() uint64 {
|
||||
return crpgenerator.prng.clock
|
||||
}
|
||||
|
||||
func (crpgenerator *CRPGenerator) Seed(seed []byte) {
|
||||
crpgenerator.prng.Seed(seed)
|
||||
}
|
||||
|
||||
func (crpgenerator *CRPGenerator) GetSeed() []byte {
|
||||
return crpgenerator.prng.seed[:]
|
||||
}
|
||||
|
||||
func (crpgenerator *CRPGenerator) SetClock(n uint64) error {
|
||||
if err := crpgenerator.prng.SetClock(n); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (crpgenerator *CRPGenerator) Clock() *ring.Poly {
|
||||
var coeff uint64
|
||||
var randomBytes []byte
|
||||
|
||||
crp := crpgenerator.context.NewPoly()
|
||||
|
||||
// Starts with 32 random bytes from the prng
|
||||
randomBytes = crpgenerator.prng.Clock()
|
||||
|
||||
for i := uint64(0); i < crpgenerator.context.N; i++ {
|
||||
|
||||
for j, qi := range crpgenerator.context.Modulus {
|
||||
|
||||
for {
|
||||
// If not enough randomBytes, generate 32 more random bytes from the PRNG
|
||||
if len(randomBytes) < 8 {
|
||||
randomBytes = crpgenerator.prng.Clock()
|
||||
}
|
||||
|
||||
// Converts 4 randomBytes into an uint64 of at most 60 bits (maximum size of the modulus)
|
||||
coeff = binary.BigEndian.Uint64(randomBytes[:8]) & crpgenerator.masks[j]
|
||||
|
||||
// Drops the used bytes
|
||||
randomBytes = randomBytes[8:]
|
||||
|
||||
// If coeff is smaller than qi, breaks
|
||||
if coeff < qi {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Assigns coeff to the ring crp coefficient
|
||||
crp.Coeffs[j][i] = coeff
|
||||
}
|
||||
}
|
||||
|
||||
return crp
|
||||
}
|
||||
215
dckks/collective_EKG.go
Normal file
215
dckks/collective_EKG.go
Normal file
@@ -0,0 +1,215 @@
|
||||
package dckks
|
||||
|
||||
import (
|
||||
"github.com/lca1/lattigo/ckks"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
"math"
|
||||
)
|
||||
|
||||
type EkgProtocol struct {
|
||||
context *ring.Context
|
||||
gaussianSampler *ring.KYSampler
|
||||
bitDecomp uint64
|
||||
bitLog uint64
|
||||
polypool *ring.Poly
|
||||
}
|
||||
|
||||
func NewEkgProtocol(context *ring.Context, bitDecomp uint64) *EkgProtocol {
|
||||
ekg := new(EkgProtocol)
|
||||
ekg.context = context
|
||||
ekg.gaussianSampler = context.NewKYSampler(3.19, 19)
|
||||
ekg.bitDecomp = bitDecomp
|
||||
ekg.bitLog = uint64(math.Ceil(float64(60) / float64(bitDecomp)))
|
||||
ekg.polypool = context.NewPoly()
|
||||
return ekg
|
||||
}
|
||||
|
||||
// Ephemeral Key u (needs to be stored among the 3 first round)
|
||||
func (ekg *EkgProtocol) NewEphemeralKey() (ephemeralKey *ring.Poly) {
|
||||
ephemeralKey = ekg.context.NewTernaryPoly()
|
||||
ekg.context.NTT(ephemeralKey, ephemeralKey)
|
||||
ekg.context.MForm(ephemeralKey, ephemeralKey)
|
||||
return
|
||||
}
|
||||
|
||||
// Ephemeral Key u (needs to be stored among the 3 first round)
|
||||
func (ekg *EkgProtocol) GenSamples(u, sk *ring.Poly, crp [][]*ring.Poly) (h [][]*ring.Poly) {
|
||||
|
||||
h = make([][]*ring.Poly, len(ekg.context.Modulus))
|
||||
|
||||
mredParams := ekg.context.GetMredParams()
|
||||
|
||||
// Given a base decomposition w_i (here the CRT decomposition)
|
||||
// computes [-u*a_i + s*w_i + e_i]
|
||||
// where a_i = crp_i
|
||||
for i, qi := range ekg.context.Modulus {
|
||||
|
||||
h[i] = make([]*ring.Poly, ekg.bitLog)
|
||||
|
||||
for w := uint64(0); w < ekg.bitLog; w++ {
|
||||
|
||||
// h = e
|
||||
h[i][w] = ekg.gaussianSampler.SampleNTTNew()
|
||||
|
||||
// h = sk*CrtBaseDecompQi + e
|
||||
for j := uint64(0); j < ekg.context.N; j++ {
|
||||
h[i][w].Coeffs[i][j] += ckks.PowerOf2(sk.Coeffs[i][j], ekg.bitDecomp*w, qi, mredParams[i])
|
||||
}
|
||||
|
||||
// h = sk*CrtBaseDecompQi + -u*a + e
|
||||
ekg.context.MulCoeffsMontgomeryAndSub(u, crp[i][w], h[i][w])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Round 2
|
||||
func (ekg *EkgProtocol) Aggregate(sk *ring.Poly, samples [][][]*ring.Poly, crp [][]*ring.Poly) (h [][][2]*ring.Poly) {
|
||||
|
||||
h = make([][][2]*ring.Poly, len(ekg.context.Modulus))
|
||||
|
||||
// Each sample is of the form [-u*a_i + s*w_i + e_i]
|
||||
// So for each element of the base decomposition w_i :
|
||||
for i := range ekg.context.Modulus {
|
||||
|
||||
h[i] = make([][2]*ring.Poly, ekg.bitLog)
|
||||
|
||||
for w := uint64(0); w < ekg.bitLog; w++ {
|
||||
|
||||
// Computes [(sum samples)*sk + e_1i, sk*a + e_2i]
|
||||
|
||||
// First Element
|
||||
h[i][w][0] = samples[0][i][w].CopyNew()
|
||||
|
||||
// Continues with the sum samples
|
||||
for j := 1; j < len(samples); j++ {
|
||||
ekg.context.AddNoMod(h[i][w][0], samples[j][i][w], h[i][w][0])
|
||||
|
||||
if j&7 == 7 {
|
||||
ekg.context.Reduce(h[i][w][0], h[i][w][0])
|
||||
}
|
||||
}
|
||||
|
||||
if (len(samples)-1)&7 != 7 {
|
||||
ekg.context.Reduce(h[i][w][0], h[i][w][0])
|
||||
}
|
||||
|
||||
// (Sum samples) * sk
|
||||
ekg.context.MulCoeffsMontgomery(h[i][w][0], sk, h[i][w][0])
|
||||
|
||||
// (Sum samples) * sk + e_1i
|
||||
ekg.gaussianSampler.SampleNTT(ekg.polypool)
|
||||
ekg.context.Add(h[i][w][0], ekg.polypool, h[i][w][0])
|
||||
|
||||
// Second Element
|
||||
|
||||
// e_2i
|
||||
h[i][w][1] = ekg.gaussianSampler.SampleNTTNew()
|
||||
// s*a + e_2i
|
||||
ekg.context.MulCoeffsMontgomeryAndAdd(sk, crp[i][w], h[i][w][1])
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ekg.polypool.Zero()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Round 3
|
||||
|
||||
//Part 1
|
||||
func (ekg *EkgProtocol) Sum(samples [][][][2]*ring.Poly) (h [][][2]*ring.Poly) {
|
||||
|
||||
h = make([][][2]*ring.Poly, len(ekg.context.Modulus))
|
||||
|
||||
for i := range ekg.context.Modulus {
|
||||
|
||||
h[i] = make([][2]*ring.Poly, ekg.bitLog)
|
||||
|
||||
for w := uint64(0); w < ekg.bitLog; w++ {
|
||||
|
||||
h[i][w][0] = samples[0][i][w][0].CopyNew()
|
||||
h[i][w][1] = samples[0][i][w][1].CopyNew()
|
||||
|
||||
for j := 1; j < len(samples); j++ {
|
||||
ekg.context.AddNoMod(h[i][w][0], samples[j][i][w][0], h[i][w][0])
|
||||
ekg.context.AddNoMod(h[i][w][1], samples[j][i][w][1], h[i][w][1])
|
||||
|
||||
if j&7 == 7 {
|
||||
ekg.context.Reduce(h[i][w][0], h[i][w][0])
|
||||
ekg.context.Reduce(h[i][w][1], h[i][w][1])
|
||||
}
|
||||
}
|
||||
if (len(samples)-1)&7 != 7 {
|
||||
ekg.context.Reduce(h[i][w][0], h[i][w][0])
|
||||
ekg.context.Reduce(h[i][w][1], h[i][w][1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Part 2
|
||||
func (ekg *EkgProtocol) KeySwitch(u, sk *ring.Poly, samples [][][2]*ring.Poly) (h1 [][]*ring.Poly) {
|
||||
|
||||
h1 = make([][]*ring.Poly, len(ekg.context.Modulus))
|
||||
|
||||
// (u_i - s_i)
|
||||
mask := ekg.context.NewPoly()
|
||||
ekg.context.Sub(u, sk, mask)
|
||||
|
||||
for i := range ekg.context.Modulus {
|
||||
|
||||
h1[i] = make([]*ring.Poly, ekg.bitLog)
|
||||
|
||||
for w := uint64(0); w < ekg.bitLog; w++ {
|
||||
|
||||
// (u - s) * (sum [x][s*a_i + e_2i]) + e3i
|
||||
h1[i][w] = ekg.gaussianSampler.SampleNTTNew()
|
||||
ekg.context.MulCoeffsMontgomeryAndAdd(mask, samples[i][w][1], h1[i][w])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Round 4
|
||||
|
||||
func (ekg *EkgProtocol) ComputeEVK(h1 [][][]*ring.Poly, h [][][2]*ring.Poly) (collectiveEVK [][][2]*ring.Poly) {
|
||||
|
||||
collectiveEVK = make([][][2]*ring.Poly, len(ekg.context.Modulus))
|
||||
|
||||
// collectiveEVK[i][0] = h[i][0] + sum(h1[i])
|
||||
// collectiveEVK[i][1] = h[i][1]
|
||||
for i := range ekg.context.Modulus {
|
||||
|
||||
collectiveEVK[i] = make([][2]*ring.Poly, ekg.bitLog)
|
||||
|
||||
for w := uint64(0); w < ekg.bitLog; w++ {
|
||||
|
||||
collectiveEVK[i][w][0] = h[i][w][0].CopyNew()
|
||||
collectiveEVK[i][w][1] = h[i][w][1].CopyNew()
|
||||
|
||||
for j := range h1 {
|
||||
ekg.context.AddNoMod(collectiveEVK[i][w][0], h1[j][i][w], collectiveEVK[i][w][0])
|
||||
|
||||
if j&7 == 7 {
|
||||
ekg.context.Reduce(collectiveEVK[i][w][0], collectiveEVK[i][w][0])
|
||||
}
|
||||
}
|
||||
|
||||
if (len(h1)-1)&7 != 7 {
|
||||
ekg.context.Reduce(collectiveEVK[i][w][0], collectiveEVK[i][w][0])
|
||||
}
|
||||
|
||||
ekg.context.MForm(collectiveEVK[i][w][0], collectiveEVK[i][w][0])
|
||||
ekg.context.MForm(collectiveEVK[i][w][1], collectiveEVK[i][w][1])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
160
dckks/collective_EKG_Naive.go
Normal file
160
dckks/collective_EKG_Naive.go
Normal file
@@ -0,0 +1,160 @@
|
||||
package dckks
|
||||
|
||||
import (
|
||||
"github.com/lca1/lattigo/ckks"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
"math"
|
||||
)
|
||||
|
||||
type EkgProtocolNaive struct {
|
||||
context *ring.Context
|
||||
gaussianSampler *ring.KYSampler
|
||||
ternarySampler *ring.TernarySampler
|
||||
bitDecomp uint64
|
||||
bitLog uint64
|
||||
polypool *ring.Poly
|
||||
}
|
||||
|
||||
func NewEkgProtocolNaive(context *ring.Context, bitDecomp uint64) *EkgProtocolNaive {
|
||||
ekg := new(EkgProtocolNaive)
|
||||
ekg.context = context
|
||||
ekg.gaussianSampler = context.NewKYSampler(3.19, 19)
|
||||
ekg.ternarySampler = context.NewTernarySampler()
|
||||
ekg.bitDecomp = bitDecomp
|
||||
ekg.bitLog = uint64(math.Ceil(float64(60) / float64(bitDecomp)))
|
||||
ekg.polypool = context.NewPoly()
|
||||
return ekg
|
||||
}
|
||||
|
||||
func (ekg *EkgProtocolNaive) GenSamples(sk *ring.Poly, pk [2]*ring.Poly) [][][2]*ring.Poly {
|
||||
|
||||
h := make([][][2]*ring.Poly, len(ekg.context.Modulus))
|
||||
|
||||
mredParams := ekg.context.GetMredParams()
|
||||
|
||||
for i, qi := range ekg.context.Modulus {
|
||||
|
||||
h[i] = make([][2]*ring.Poly, ekg.bitLog)
|
||||
|
||||
for w := uint64(0); w < ekg.bitLog; w++ {
|
||||
|
||||
// h_0 = e0
|
||||
h[i][w][0] = ekg.gaussianSampler.SampleNTTNew()
|
||||
// h_1 = e1
|
||||
h[i][w][1] = ekg.gaussianSampler.SampleNTTNew()
|
||||
|
||||
// h_0 = e0 + [sk*w*(qiBarre*qiStar)%qi = 1<<w, else 0]
|
||||
for j := uint64(0); j < ekg.context.N; j++ {
|
||||
h[i][w][0].Coeffs[i][j] += ckks.PowerOf2(sk.Coeffs[i][j], ekg.bitDecomp*w, qi, mredParams[i])
|
||||
}
|
||||
|
||||
// u
|
||||
ekg.ternarySampler.SampleMontgomeryNTT(ekg.polypool)
|
||||
|
||||
// h_0 = pk_0 * u + e0 + sk*w*(qiBarre*qiStar)%qi
|
||||
ekg.context.MulCoeffsMontgomeryAndAdd(pk[0], ekg.polypool, h[i][w][0])
|
||||
// h_1 = pk_1 * u + e1 + sk*w*(qiBarre*qiStar)%qi
|
||||
ekg.context.MulCoeffsMontgomeryAndAdd(pk[1], ekg.polypool, h[i][w][1])
|
||||
}
|
||||
}
|
||||
|
||||
ekg.polypool.Zero()
|
||||
|
||||
return h
|
||||
|
||||
}
|
||||
|
||||
func (ekg *EkgProtocolNaive) Aggregate(sk *ring.Poly, pk [2]*ring.Poly, samples [][][][2]*ring.Poly) [][][2]*ring.Poly {
|
||||
|
||||
h := make([][][2]*ring.Poly, len(ekg.context.Modulus))
|
||||
|
||||
for i := range h {
|
||||
|
||||
h[i] = make([][2]*ring.Poly, ekg.bitLog)
|
||||
|
||||
for w := uint64(0); w < ekg.bitLog; w++ {
|
||||
|
||||
h[i][w][0] = samples[0][i][w][0].CopyNew()
|
||||
h[i][w][1] = samples[0][i][w][1].CopyNew()
|
||||
|
||||
// h_0 = sum(samples[0])
|
||||
// h_1 = sum(samples[1])
|
||||
for j := 1; j < len(samples); j++ {
|
||||
ekg.context.AddNoMod(h[i][w][0], samples[j][i][w][0], h[i][w][0])
|
||||
ekg.context.AddNoMod(h[i][w][1], samples[j][i][w][1], h[i][w][1])
|
||||
|
||||
if j&7 == 7 {
|
||||
ekg.context.Reduce(h[i][w][0], h[i][w][0])
|
||||
ekg.context.Reduce(h[i][w][1], h[i][w][1])
|
||||
}
|
||||
}
|
||||
|
||||
if (len(samples)-1)&7 == 7 {
|
||||
ekg.context.Reduce(h[i][w][0], h[i][w][0])
|
||||
ekg.context.Reduce(h[i][w][1], h[i][w][1])
|
||||
}
|
||||
|
||||
// h_0 = sum(samples[0]) * sk
|
||||
// h_1 = sum(samples[1]) * sk
|
||||
ekg.context.MulCoeffsMontgomery(h[i][w][0], sk, h[i][w][0])
|
||||
ekg.context.MulCoeffsMontgomery(h[i][w][1], sk, h[i][w][1])
|
||||
|
||||
// v
|
||||
ekg.ternarySampler.SampleMontgomeryNTT(ekg.polypool)
|
||||
|
||||
// h_0 = sum(samples[0]) * sk + pk0 * v
|
||||
ekg.context.MulCoeffsMontgomeryAndAdd(pk[0], ekg.polypool, h[i][w][0])
|
||||
|
||||
// h_1 = sum(samples[1]) * sk + pk1 * v
|
||||
ekg.context.MulCoeffsMontgomeryAndAdd(pk[1], ekg.polypool, h[i][w][1])
|
||||
|
||||
// h_0 = sum(samples[0]) * sk + pk0 * v + e2
|
||||
ekg.gaussianSampler.SampleNTT(ekg.polypool)
|
||||
ekg.context.Add(h[i][w][0], ekg.polypool, h[i][w][0])
|
||||
|
||||
// h_1 = sum(samples[1]) * sk + pk1 * v + e3
|
||||
ekg.gaussianSampler.SampleNTT(ekg.polypool)
|
||||
ekg.context.Add(h[i][w][1], ekg.polypool, h[i][w][1])
|
||||
}
|
||||
}
|
||||
|
||||
ekg.polypool.Zero()
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (ekg *EkgProtocolNaive) Finalize(h [][][][2]*ring.Poly) [][][2]*ring.Poly {
|
||||
|
||||
evaluationKey := make([][][2]*ring.Poly, len(ekg.context.Modulus))
|
||||
|
||||
for i := range ekg.context.Modulus {
|
||||
|
||||
evaluationKey[i] = make([][2]*ring.Poly, ekg.bitLog)
|
||||
|
||||
for w := uint64(0); w < ekg.bitLog; w++ {
|
||||
|
||||
evaluationKey[i][w][0] = h[0][i][w][0].CopyNew()
|
||||
evaluationKey[i][w][1] = h[0][i][w][1].CopyNew()
|
||||
|
||||
for j := 1; j < len(h); j++ {
|
||||
ekg.context.AddNoMod(evaluationKey[i][w][0], h[j][i][w][0], evaluationKey[i][w][0])
|
||||
ekg.context.AddNoMod(evaluationKey[i][w][1], h[j][i][w][1], evaluationKey[i][w][1])
|
||||
|
||||
if j&7 == 7 {
|
||||
ekg.context.Reduce(evaluationKey[i][w][0], evaluationKey[i][w][0])
|
||||
ekg.context.Reduce(evaluationKey[i][w][1], evaluationKey[i][w][1])
|
||||
}
|
||||
}
|
||||
|
||||
if (len(h)-1)&7 == 7 {
|
||||
ekg.context.Reduce(evaluationKey[i][w][0], evaluationKey[i][w][0])
|
||||
ekg.context.Reduce(evaluationKey[i][w][1], evaluationKey[i][w][1])
|
||||
}
|
||||
|
||||
ekg.context.MForm(evaluationKey[i][w][0], evaluationKey[i][w][0])
|
||||
ekg.context.MForm(evaluationKey[i][w][1], evaluationKey[i][w][1])
|
||||
}
|
||||
}
|
||||
|
||||
return evaluationKey
|
||||
}
|
||||
53
dckks/collective_KG.go
Normal file
53
dckks/collective_KG.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package dckks
|
||||
|
||||
import (
|
||||
"github.com/lca1/lattigo/ckks"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
)
|
||||
|
||||
type CKG struct {
|
||||
context *ring.Context
|
||||
gaussianSampler *ring.KYSampler
|
||||
|
||||
share *ring.Poly
|
||||
cpk [2]*ring.Poly
|
||||
}
|
||||
|
||||
func NewCKG(context *ring.Context, crs *ring.Poly) *CKG {
|
||||
ckg := new(CKG)
|
||||
ckg.context = context
|
||||
ckg.gaussianSampler = context.NewKYSampler(3.19, 19)
|
||||
|
||||
ckg.cpk[0] = context.NewPoly()
|
||||
ckg.cpk[1] = crs.CopyNew()
|
||||
|
||||
return ckg
|
||||
}
|
||||
|
||||
func (ckg *CKG) GenShare(sk *ring.Poly) error {
|
||||
|
||||
// -(sk * crs) + e
|
||||
ckg.share = ckg.gaussianSampler.SampleNTTNew()
|
||||
ckg.context.MulCoeffsMontgomeryAndSub(sk, ckg.cpk[1], ckg.share)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ckg *CKG) GetShare() *ring.Poly {
|
||||
return ckg.share
|
||||
}
|
||||
|
||||
func (ckg *CKG) AggregateShares(shares []*ring.Poly) error {
|
||||
|
||||
for i := 0; i < len(shares); i++ {
|
||||
ckg.context.Add(ckg.cpk[0], shares[i], ckg.cpk[0])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ckg *CKG) Finalize() (*ckks.PublicKey, error) {
|
||||
collectivePk := new(ckks.PublicKey)
|
||||
collectivePk.Set(ckg.cpk)
|
||||
return collectivePk, nil
|
||||
}
|
||||
57
dckks/collective_KS.go
Normal file
57
dckks/collective_KS.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package dckks
|
||||
|
||||
import (
|
||||
"github.com/lca1/lattigo/ring"
|
||||
)
|
||||
|
||||
type CKS struct {
|
||||
context *ring.Context
|
||||
|
||||
sigmaSmudging float64
|
||||
gaussianSampler *ring.KYSampler
|
||||
|
||||
deltaSk *ring.Poly
|
||||
|
||||
polypool *ring.Poly
|
||||
}
|
||||
|
||||
func NewCKS(skInput, skOutput *ring.Poly, context *ring.Context, sigmaSmudging float64) *CKS {
|
||||
|
||||
cks := new(CKS)
|
||||
cks.context = context
|
||||
|
||||
cks.sigmaSmudging = sigmaSmudging
|
||||
cks.gaussianSampler = context.NewKYSampler(sigmaSmudging, int(6*sigmaSmudging))
|
||||
|
||||
cks.deltaSk = cks.context.NewPoly()
|
||||
context.Sub(skInput, skOutput, cks.deltaSk)
|
||||
cks.polypool = context.NewPoly()
|
||||
return cks
|
||||
}
|
||||
|
||||
func (cks *CKS) KeySwitch(c1 *ring.Poly) *ring.Poly {
|
||||
|
||||
h := c1.CopyNew()
|
||||
|
||||
cks.context.MulCoeffsMontgomery(h, cks.deltaSk, h)
|
||||
cks.gaussianSampler.SampleNTT(cks.polypool)
|
||||
cks.context.Add(h, cks.polypool, h)
|
||||
cks.polypool.Zero()
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (cks *CKS) Aggregate(c0 *ring.Poly, h []*ring.Poly) {
|
||||
|
||||
for i := range h {
|
||||
cks.context.AddNoMod(c0, h[i], c0)
|
||||
|
||||
if i&7 == 1 {
|
||||
cks.context.Reduce(c0, c0)
|
||||
}
|
||||
}
|
||||
|
||||
if len(h)&7 != 7 {
|
||||
cks.context.Reduce(c0, c0)
|
||||
}
|
||||
}
|
||||
88
dckks/collective_PKS.go
Normal file
88
dckks/collective_PKS.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package dckks
|
||||
|
||||
import (
|
||||
"github.com/lca1/lattigo/ring"
|
||||
)
|
||||
|
||||
type PCKS struct {
|
||||
context *ring.Context
|
||||
|
||||
sigmaSmudging float64
|
||||
gaussianSamplerSmudge *ring.KYSampler
|
||||
gaussianSampler *ring.KYSampler
|
||||
ternarySampler *ring.TernarySampler
|
||||
|
||||
skInput *ring.Poly
|
||||
pkOutput [2]*ring.Poly
|
||||
|
||||
polypool *ring.Poly
|
||||
}
|
||||
|
||||
func NewPCKS(skInput *ring.Poly, pkOutput [2]*ring.Poly, context *ring.Context, sigmaSmudging float64) *PCKS {
|
||||
|
||||
pcks := new(PCKS)
|
||||
pcks.context = context
|
||||
pcks.sigmaSmudging = sigmaSmudging
|
||||
|
||||
pcks.gaussianSamplerSmudge = context.NewKYSampler(sigmaSmudging, int(6*sigmaSmudging))
|
||||
pcks.gaussianSampler = context.NewKYSampler(3.19, 19)
|
||||
pcks.ternarySampler = context.NewTernarySampler()
|
||||
|
||||
pcks.skInput = skInput
|
||||
pcks.pkOutput = pkOutput
|
||||
|
||||
pcks.polypool = context.NewPoly()
|
||||
|
||||
return pcks
|
||||
}
|
||||
|
||||
func (pcks *PCKS) KeySwitch(ct1 *ring.Poly) (h [2]*ring.Poly) {
|
||||
|
||||
// h_0 = pk_0 (NTT)
|
||||
h[0] = pcks.pkOutput[0].CopyNew()
|
||||
// h_1 = pk_1 (NTT)
|
||||
h[1] = pcks.pkOutput[1].CopyNew()
|
||||
|
||||
//u_i
|
||||
pcks.ternarySampler.SampleMontgomeryNTT(pcks.polypool)
|
||||
|
||||
// h_0 = u_i * pk_0 (NTT)
|
||||
pcks.context.MulCoeffsMontgomery(h[0], pcks.polypool, h[0])
|
||||
// h_1 = u_i * pk_1 (NTT)
|
||||
pcks.context.MulCoeffsMontgomery(h[1], pcks.polypool, h[1])
|
||||
|
||||
// h0 = u_i * pk_0 + s_i*c_1 (NTT)
|
||||
pcks.context.MulCoeffsMontgomeryAndAdd(ct1, pcks.skInput, h[0])
|
||||
|
||||
// h_0 = InvNTT(s_i*c_1 + u_i * pk_0) + e0
|
||||
pcks.gaussianSamplerSmudge.SampleNTT(pcks.polypool)
|
||||
pcks.context.Add(h[0], pcks.polypool, h[0])
|
||||
|
||||
// h_1 = InvNTT(u_i * pk_1) + e1
|
||||
pcks.gaussianSampler.SampleNTT(pcks.polypool)
|
||||
pcks.context.Add(h[1], pcks.polypool, h[1])
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (pcks *PCKS) Aggregate(ct []*ring.Poly, h [][2]*ring.Poly) {
|
||||
|
||||
pcks.context.AddNoMod(ct[0], h[0][0], ct[0])
|
||||
ct[1] = h[0][1].CopyNew()
|
||||
|
||||
for i := 1; i < len(h); i++ {
|
||||
pcks.context.AddNoMod(ct[0], h[i][0], ct[0])
|
||||
pcks.context.AddNoMod(ct[1], h[i][1], ct[1])
|
||||
|
||||
if i&7 == 7 {
|
||||
pcks.context.Reduce(ct[0], ct[0])
|
||||
pcks.context.Reduce(ct[1], ct[1])
|
||||
}
|
||||
}
|
||||
|
||||
if len(h)&7 != 7 {
|
||||
pcks.context.Reduce(ct[0], ct[0])
|
||||
pcks.context.Reduce(ct[1], ct[1])
|
||||
}
|
||||
|
||||
}
|
||||
257
dckks/dckks_benchmark_test.go
Normal file
257
dckks/dckks_benchmark_test.go
Normal file
@@ -0,0 +1,257 @@
|
||||
package dckks
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/lca1/lattigo/ckks"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
"log"
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type benchParams struct {
|
||||
parties uint64
|
||||
logN uint64
|
||||
logQ uint64
|
||||
levels uint64
|
||||
logScale uint64
|
||||
sigma float64
|
||||
sigmaSmudging float64
|
||||
bdc uint64
|
||||
}
|
||||
|
||||
type benchContext struct {
|
||||
ckkscontext *ckks.CkksContext
|
||||
sk0 *ckks.SecretKey
|
||||
sk1 *ckks.SecretKey
|
||||
pk0 *ckks.PublicKey
|
||||
pk1 *ckks.PublicKey
|
||||
cprng *CRPGenerator
|
||||
}
|
||||
|
||||
func Benchmark_DCKKSScheme(b *testing.B) {
|
||||
|
||||
var err error
|
||||
|
||||
params := []benchParams{
|
||||
|
||||
{parties: 5, logN: 14, logQ: 40, levels: 8, logScale: 40, sigma: 3.2, sigmaSmudging: 6.4, bdc: 60},
|
||||
//{parties : 5, logN: 15, logQ: 40, levels: 16, logScale: 40, sigma: 3.2, bdc: 60},
|
||||
}
|
||||
|
||||
for _, param := range params {
|
||||
|
||||
benchcontext := new(benchContext)
|
||||
|
||||
log.Printf("Benchmarks for parties=%d/logN=%d/logQ=%d/levels=%d/sigma=%.2f/sigmaSmudging=%.2f/bdc=%d", param.logN, param.logQ, param.logScale, param.levels, param.sigma, param.sigmaSmudging, param.bdc)
|
||||
if benchcontext.ckkscontext, err = ckks.NewCkksContext(param.logN, param.logQ, param.logScale, param.levels, param.sigma); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
kgen := benchcontext.ckkscontext.NewKeyGenerator()
|
||||
|
||||
benchcontext.sk0, benchcontext.pk0, err = kgen.NewKeyPair()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
benchcontext.sk1, benchcontext.pk1, err = kgen.NewKeyPair()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
benchcontext.cprng, err = NewCRPGenerator(nil, benchcontext.ckkscontext.ContextKeys())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
benchcontext.cprng.Seed([]byte{})
|
||||
|
||||
bench_EKG(param, benchcontext, b)
|
||||
bench_EKGNaive(param, benchcontext, b)
|
||||
bench_CKG(param, benchcontext, b)
|
||||
bench_CKS(param, benchcontext, b)
|
||||
bench_PCKS(param, benchcontext, b)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func bench_EKG(params benchParams, context *benchContext, b *testing.B) {
|
||||
// EKG
|
||||
bitLog := uint64(math.Ceil(float64(60) / float64(params.bdc)))
|
||||
|
||||
EkgProtocol := NewEkgProtocol(context.ckkscontext.ContextKeys(), params.bdc)
|
||||
|
||||
crp := make([][]*ring.Poly, params.levels)
|
||||
|
||||
for i := uint64(0); i < params.levels; i++ {
|
||||
crp[i] = make([]*ring.Poly, bitLog)
|
||||
for j := uint64(0); j < bitLog; j++ {
|
||||
crp[i][j] = context.cprng.Clock()
|
||||
}
|
||||
}
|
||||
|
||||
samples := make([][][]*ring.Poly, params.parties)
|
||||
for i := uint64(0); i < params.parties; i++ {
|
||||
samples[i] = make([][]*ring.Poly, params.levels)
|
||||
samples[i] = EkgProtocol.GenSamples(context.sk0.Get(), context.sk1.Get(), crp)
|
||||
}
|
||||
|
||||
aggregatedSamples := make([][][][2]*ring.Poly, params.parties)
|
||||
for i := uint64(0); i < params.parties; i++ {
|
||||
aggregatedSamples[i] = EkgProtocol.Aggregate(context.sk1.Get(), samples, crp)
|
||||
}
|
||||
|
||||
keySwitched := make([][][]*ring.Poly, params.parties)
|
||||
|
||||
sum := EkgProtocol.Sum(aggregatedSamples)
|
||||
for i := uint64(0); i < params.parties; i++ {
|
||||
keySwitched[i] = EkgProtocol.KeySwitch(context.sk0.Get(), context.sk1.Get(), sum)
|
||||
}
|
||||
|
||||
//EKG_V2_Round_0
|
||||
b.Run(fmt.Sprintf("EKG_Round0"), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
EkgProtocol.GenSamples(context.sk0.Get(), context.sk1.Get(), crp)
|
||||
}
|
||||
})
|
||||
|
||||
//EKG_V2_Round_1
|
||||
b.Run(fmt.Sprintf("EKG_Round1"), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
EkgProtocol.Aggregate(context.sk1.Get(), samples, crp)
|
||||
}
|
||||
})
|
||||
|
||||
//EKG_V2_Round_2
|
||||
b.Run(fmt.Sprintf("EKG_Round2"), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
EkgProtocol.KeySwitch(context.sk1.Get(), context.sk1.Get(), EkgProtocol.Sum(aggregatedSamples))
|
||||
}
|
||||
})
|
||||
|
||||
//EKG_V2_Round_3
|
||||
b.Run(fmt.Sprintf("EKG_Round3"), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
EkgProtocol.ComputeEVK(keySwitched, sum)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func bench_EKGNaive(params benchParams, context *benchContext, b *testing.B) {
|
||||
// EKG_Naive
|
||||
ekgV2Naive := NewEkgProtocolNaive(context.ckkscontext.ContextKeys(), params.bdc)
|
||||
|
||||
// [nParties][CrtDecomp][WDecomp][2]
|
||||
samples := make([][][][2]*ring.Poly, params.parties)
|
||||
for i := uint64(0); i < params.parties; i++ {
|
||||
samples[i] = ekgV2Naive.GenSamples(context.sk0.Get(), context.pk0.Get())
|
||||
}
|
||||
|
||||
aggregatedSamples := make([][][][2]*ring.Poly, params.parties)
|
||||
for i := uint64(0); i < params.parties; i++ {
|
||||
aggregatedSamples[i] = ekgV2Naive.Aggregate(context.sk0.Get(), context.pk0.Get(), samples)
|
||||
}
|
||||
|
||||
//EKG_V2_Naive_Round_0
|
||||
b.Run(fmt.Sprintf("EKG_Naive_Round0"), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ekgV2Naive.GenSamples(context.sk0.Get(), context.pk1.Get())
|
||||
}
|
||||
})
|
||||
|
||||
//EKG_V2_Naive_Round_1
|
||||
b.Run(fmt.Sprintf("EKG_Naive_Round1"), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ekgV2Naive.Aggregate(context.sk0.Get(), context.pk1.Get(), samples)
|
||||
}
|
||||
})
|
||||
|
||||
//EKG_V2_Naive_Round_2
|
||||
b.Run(fmt.Sprintf("EKG_Naive_Round2"), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ekgV2Naive.Finalize(aggregatedSamples)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func bench_CKG(params benchParams, context *benchContext, b *testing.B) {
|
||||
|
||||
//CKG
|
||||
ckgInstance := NewCKG(context.ckkscontext.ContextKeys(), context.cprng.Clock())
|
||||
ckgInstance.GenShare(context.sk0.Get())
|
||||
|
||||
shares := make([]*ring.Poly, params.parties)
|
||||
for i := uint64(0); i < params.parties; i++ {
|
||||
shares[i] = ckgInstance.GetShare()
|
||||
}
|
||||
|
||||
// CKG_Round_0
|
||||
b.Run(fmt.Sprintf("CKG_Round0"), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ckgInstance.GenShare(context.sk0.Get())
|
||||
}
|
||||
})
|
||||
|
||||
// CKG_Round_1
|
||||
b.Run(fmt.Sprintf("CKG_Round1"), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ckgInstance.AggregateShares(shares)
|
||||
//ckgInstance.Finalize()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func bench_CKS(params benchParams, context *benchContext, b *testing.B) {
|
||||
//CKS
|
||||
|
||||
cksInstance := NewCKS(context.sk0.Get(), context.sk1.Get(), context.ckkscontext.ContextKeys(), params.sigmaSmudging)
|
||||
|
||||
ciphertext := context.ckkscontext.NewRandomCiphertext(1, params.levels-1, params.logScale)
|
||||
|
||||
hi := make([]*ring.Poly, params.parties)
|
||||
for i := uint64(0); i < params.parties; i++ {
|
||||
hi[i] = cksInstance.KeySwitch(ciphertext.Value()[1])
|
||||
}
|
||||
|
||||
// CKS_Round_0
|
||||
b.Run(fmt.Sprintf("CKS_Round0"), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
cksInstance.KeySwitch(ciphertext.Value()[1])
|
||||
}
|
||||
})
|
||||
|
||||
// CKS_Round_1
|
||||
b.Run(fmt.Sprintf("CKS_Round1"), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
cksInstance.Aggregate(ciphertext.Value()[0], hi)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func bench_PCKS(params benchParams, context *benchContext, b *testing.B) {
|
||||
//CKS_Trustless
|
||||
pcks := NewPCKS(context.sk0.Get(), context.pk1.Get(), context.ckkscontext.ContextKeys(), params.sigmaSmudging)
|
||||
|
||||
ciphertext := context.ckkscontext.NewRandomCiphertext(1, params.levels-1, params.logScale)
|
||||
|
||||
hi := make([][2]*ring.Poly, params.parties)
|
||||
for i := uint64(0); i < params.parties; i++ {
|
||||
hi[i] = pcks.KeySwitch(ciphertext.Value()[1])
|
||||
}
|
||||
|
||||
// CKS_Trustless_Round_0
|
||||
b.Run(fmt.Sprintf("PCKS_Round0"), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
pcks.KeySwitch(ciphertext.Value()[1])
|
||||
}
|
||||
})
|
||||
|
||||
// CKS_Trustless_Round_1
|
||||
b.Run(fmt.Sprintf("PCKS_Round1"), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
pcks.Aggregate(ciphertext.Value(), hi)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
471
dckks/dckks_test.go
Normal file
471
dckks/dckks_test.go
Normal file
@@ -0,0 +1,471 @@
|
||||
package dckks
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/lca1/lattigo/ckks"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
"log"
|
||||
"math"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func randomFloat(min, max float64) float64 {
|
||||
return min + rand.Float64()*(max-min)
|
||||
}
|
||||
|
||||
func randomComplex(min, max float64) complex128 {
|
||||
return complex(randomFloat(min, max), randomFloat(min, max))
|
||||
}
|
||||
|
||||
func Test_DBFVScheme(t *testing.T) {
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
//sigmaSmudging := 6.36
|
||||
|
||||
var err error
|
||||
|
||||
var parties, logN, logQ, levels, logScale, bdc uint64
|
||||
parties = 5
|
||||
logN = 9
|
||||
logQ = 49
|
||||
levels = 12
|
||||
sigma := 3.19
|
||||
logScale = 40
|
||||
bdc = 20
|
||||
|
||||
var ckkscontext *ckks.CkksContext
|
||||
|
||||
log.Printf("Generating CkksContext for logN=%d/logQ=%d/levels=%d/logScale=%d/sigma=%f", logN, logQ, levels, logScale, sigma)
|
||||
if ckkscontext, err = ckks.NewCkksContext(logN, logQ, logScale, levels, sigma); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
kgen := ckkscontext.NewKeyGenerator()
|
||||
|
||||
evaluator := ckkscontext.NewEvaluator()
|
||||
|
||||
context := ckkscontext.ContextKeys()
|
||||
|
||||
coeffsWant := make([]complex128, ckkscontext.Slots())
|
||||
for i := uint64(0); i < ckkscontext.Slots(); i++ {
|
||||
coeffsWant[i] = randomComplex(-1, 1)
|
||||
}
|
||||
|
||||
plaintextWant := ckkscontext.NewPlaintext(levels-1, logScale)
|
||||
if err = plaintextWant.EncodeComplex(coeffsWant); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
ciphertextTest := ckkscontext.NewCiphertext(1, levels-1, logScale)
|
||||
|
||||
crpGenerators := make([]*CRPGenerator, parties)
|
||||
for i := uint64(0); i < parties; i++ {
|
||||
crpGenerators[i], err = NewCRPGenerator(nil, context)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
crpGenerators[i].Seed([]byte{})
|
||||
}
|
||||
|
||||
// SecretKeys
|
||||
sk0_shards := make([]*ckks.SecretKey, parties)
|
||||
sk1_shards := make([]*ckks.SecretKey, parties)
|
||||
tmp0 := context.NewPoly()
|
||||
tmp1 := context.NewPoly()
|
||||
|
||||
for i := uint64(0); i < parties; i++ {
|
||||
sk0_shards[i] = kgen.NewSecretKey()
|
||||
sk1_shards[i] = kgen.NewSecretKey()
|
||||
context.Add(tmp0, sk0_shards[i].Get(), tmp0)
|
||||
context.Add(tmp1, sk1_shards[i].Get(), tmp1)
|
||||
}
|
||||
|
||||
sk0 := new(ckks.SecretKey)
|
||||
sk1 := new(ckks.SecretKey)
|
||||
|
||||
sk0.Set(tmp0)
|
||||
sk1.Set(tmp1)
|
||||
|
||||
// Publickeys
|
||||
pk0, err := kgen.NewPublicKey(sk0)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
pk1, err := kgen.NewPublicKey(sk1)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
pk1 = pk1
|
||||
|
||||
// Encryptors
|
||||
encryptor_pk0, err := ckkscontext.NewEncryptor(pk0)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Decryptors
|
||||
decryptor_sk0, err := ckkscontext.NewDecryptor(sk0)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
decryptor_sk1, err := ckkscontext.NewDecryptor(sk1)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
decryptor_sk1 = decryptor_sk1
|
||||
|
||||
// Reference ciphertext
|
||||
ciphertext, err := encryptor_pk0.EncryptNew(plaintextWant)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
coeffsMul := make([]complex128, ckkscontext.Slots())
|
||||
for i := uint64(0); i < ckkscontext.Slots(); i++ {
|
||||
coeffsMul[i] = coeffsWant[i] * coeffsWant[i]
|
||||
}
|
||||
|
||||
evaluator.MulRelin(ciphertext, ciphertext, nil, ciphertext)
|
||||
|
||||
t.Run(fmt.Sprintf("parties=%d/logN=%d/logQ=%d/levels=%d/logScale=%d/CRS_PRNG", parties, logN, logQ, levels, logScale), func(t *testing.T) {
|
||||
|
||||
Ha, _ := NewPRNG([]byte{})
|
||||
Hb, _ := NewPRNG([]byte{})
|
||||
|
||||
// Random 32 byte seed
|
||||
seed1 := []byte{0x48, 0xc3, 0x31, 0x12, 0x74, 0x98, 0xd3, 0xf2,
|
||||
0x7b, 0x15, 0x15, 0x9b, 0x50, 0xc4, 0x9c, 0x00,
|
||||
0x7d, 0xa5, 0xea, 0x68, 0x1f, 0xed, 0x4f, 0x99,
|
||||
0x54, 0xc0, 0x52, 0xc0, 0x75, 0xff, 0xf7, 0x5c}
|
||||
|
||||
// New reseed of the PRNG after one clock cycle with the seed1
|
||||
seed2 := []byte{250, 228, 6, 63, 97, 110, 68, 153,
|
||||
147, 236, 236, 37, 152, 89, 129, 32,
|
||||
185, 5, 221, 180, 160, 217, 247, 201,
|
||||
211, 188, 160, 163, 176, 83, 83, 138}
|
||||
|
||||
Ha.Seed(seed1)
|
||||
Hb.Seed(append(seed1, seed2...)) //Append works since blake2b hashes blocks of 512 bytes
|
||||
|
||||
Ha.SetClock(256)
|
||||
Hb.SetClock(255)
|
||||
|
||||
a := Ha.Clock()
|
||||
b := Hb.Clock()
|
||||
|
||||
for i := 0; i < 32; i++ {
|
||||
if a[i] != b[i] {
|
||||
t.Errorf("error : error prng")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
crs_generator_1, _ := NewCRPGenerator(nil, context)
|
||||
crs_generator_2, _ := NewCRPGenerator(nil, context)
|
||||
|
||||
crs_generator_1.Seed(seed1)
|
||||
crs_generator_2.Seed(append(seed1, seed2...)) //Append works since blake2b hashes blocks of 512 bytes
|
||||
|
||||
crs_generator_1.SetClock(256)
|
||||
crs_generator_2.SetClock(255)
|
||||
|
||||
p0 := crs_generator_1.Clock()
|
||||
p1 := crs_generator_2.Clock()
|
||||
|
||||
if ckkscontext.ContextKeys().Equal(p0, p1) != true {
|
||||
t.Errorf("error : crs prng generator")
|
||||
}
|
||||
})
|
||||
|
||||
// EKG_Naive
|
||||
|
||||
t.Run(fmt.Sprintf("parties=%d/logN=%d/logQ=%d/levels=%d/logScale=%d/bdc=%d/EKG", parties, logN, logQ, levels, logScale, bdc), func(t *testing.T) {
|
||||
|
||||
bitLog := uint64(math.Ceil(float64(60) / float64(bdc)))
|
||||
|
||||
// Each party instantiate an ekg naive protocole
|
||||
ekg := make([]*EkgProtocol, parties)
|
||||
ephemeralKeys := make([]*ring.Poly, parties)
|
||||
crp := make([][][]*ring.Poly, parties)
|
||||
|
||||
for i := uint64(0); i < parties; i++ {
|
||||
|
||||
ekg[i] = NewEkgProtocol(context, bdc)
|
||||
ephemeralKeys[i] = ekg[i].NewEphemeralKey()
|
||||
crp[i] = make([][]*ring.Poly, len(context.Modulus))
|
||||
|
||||
for j := 0; j < len(context.Modulus); j++ {
|
||||
crp[i][j] = make([]*ring.Poly, bitLog)
|
||||
for u := uint64(0); u < bitLog; u++ {
|
||||
crp[i][j][u] = crpGenerators[i].Clock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
evk := test_EKG_Protocol(parties, ekg, sk0_shards, ephemeralKeys, crp)
|
||||
|
||||
rlk, err := kgen.SetRelinKeys(evk[0], bdc)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := evaluator.Relinearize(ciphertext, rlk, ciphertextTest); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
verify_test_vectors(decryptor_sk0, coeffsMul, ciphertextTest, t)
|
||||
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("parties=%d/logN=%d/logQ=%d/levels=%d/logScale=%d/bdc=%d/EKG_NAIVE", parties, logN, logQ, levels, logScale, bdc), func(t *testing.T) {
|
||||
|
||||
// Each party instantiate an ekg naive protocole
|
||||
ekgNaive := make([]*EkgProtocolNaive, parties)
|
||||
for i := uint64(0); i < parties; i++ {
|
||||
ekgNaive[i] = NewEkgProtocolNaive(context, bdc)
|
||||
}
|
||||
|
||||
evk := test_EKG_Protocol_Naive(parties, sk0_shards, pk0, ekgNaive)
|
||||
|
||||
rlk, err := kgen.SetRelinKeys(evk[0], bdc)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := evaluator.Relinearize(ciphertext, rlk, ciphertextTest); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
verify_test_vectors(decryptor_sk0, coeffsMul, ciphertextTest, t)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("parties=%d/logN=%d/logQ=%d/levels=%d/logScale=%d/CKG", parties, logN, logQ, levels, logScale), func(t *testing.T) {
|
||||
|
||||
crp := make([]*ring.Poly, parties)
|
||||
for i := uint64(0); i < parties; i++ {
|
||||
crp[i] = crpGenerators[i].Clock()
|
||||
}
|
||||
|
||||
ckg := make([]*CKG, parties)
|
||||
for i := uint64(0); i < parties; i++ {
|
||||
ckg[i] = NewCKG(context, crp[i])
|
||||
}
|
||||
|
||||
// Each party creates a new CKG instance
|
||||
shares := make([]*ring.Poly, parties)
|
||||
for i := uint64(0); i < parties; i++ {
|
||||
ckg[i].GenShare(sk0_shards[i].Get())
|
||||
shares[i] = ckg[i].GetShare()
|
||||
}
|
||||
|
||||
pkTest := make([]*ckks.PublicKey, parties)
|
||||
for i := uint64(0); i < parties; i++ {
|
||||
ckg[i].AggregateShares(shares)
|
||||
pkTest[i], err = ckg[i].Finalize()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Verifies that all parties have the same share collective public key
|
||||
for i := uint64(1); i < parties; i++ {
|
||||
if context.Equal(pkTest[0].Get()[0], pkTest[i].Get()[0]) != true || ckkscontext.ContextKeys().Equal(pkTest[0].Get()[1], pkTest[i].Get()[1]) != true {
|
||||
t.Errorf("error : ckg protocol, cpk establishement")
|
||||
}
|
||||
}
|
||||
|
||||
// Verifies that decrypt((encryptp(collectiveSk, m), collectivePk) = m
|
||||
encryptorTest, err := ckkscontext.NewEncryptor(pkTest[0])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
ciphertextTest, err := encryptorTest.EncryptNew(plaintextWant)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
verify_test_vectors(decryptor_sk0, coeffsWant, ciphertextTest, t)
|
||||
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("parties=%d/logN=%d/logQ=%d/levels=%d/logScale=%d/CKS", parties, logN, logQ, levels, logScale), func(t *testing.T) {
|
||||
|
||||
ciphertext, err := encryptor_pk0.EncryptNew(plaintextWant)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
ciphertexts := make([]*ckks.Ciphertext, parties)
|
||||
for i := uint64(0); i < parties; i++ {
|
||||
ciphertexts[i] = ciphertext.CopyNew().(*ckks.Ciphertext)
|
||||
}
|
||||
|
||||
// Each party creates its CKS instance with deltaSk = si-si'
|
||||
cks := make([]*CKS, parties)
|
||||
for i := uint64(0); i < parties; i++ {
|
||||
cks[i] = NewCKS(sk0_shards[i].Get(), sk1_shards[i].Get(), context, 6.36)
|
||||
}
|
||||
|
||||
// Each party computes its hi share from the shared ciphertext
|
||||
// Each party encodes its share and sends it to the other n-1 parties
|
||||
hi := make([]*ring.Poly, parties)
|
||||
for i := uint64(0); i < parties; i++ {
|
||||
hi[i] = cks[i].KeySwitch(ciphertexts[i].Value()[1])
|
||||
}
|
||||
// Each party receive the shares n-1 shares from the other parties and decodes them
|
||||
for i := uint64(0); i < parties; i++ {
|
||||
// Then keyswitch the ciphertext with the decoded shares
|
||||
cks[i].Aggregate(ciphertexts[i].Value()[0], hi)
|
||||
}
|
||||
|
||||
for i := uint64(0); i < parties; i++ {
|
||||
|
||||
verify_test_vectors(decryptor_sk1, coeffsWant, ciphertexts[i], t)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("parties=%d/logN=%d/logQ=%d/levels=%d/logScale=%d/PCKS", parties, logN, logQ, levels, logScale), func(t *testing.T) {
|
||||
|
||||
ciphertext, err := encryptor_pk0.EncryptNew(plaintextWant)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
ciphertexts := make([]*ckks.Ciphertext, parties)
|
||||
for i := uint64(0); i < parties; i++ {
|
||||
ciphertexts[i] = ciphertext.CopyNew().(*ckks.Ciphertext)
|
||||
}
|
||||
|
||||
pcks := make([]*PCKS, parties)
|
||||
for i := uint64(0); i < parties; i++ {
|
||||
pcks[i] = NewPCKS(sk0_shards[i].Get(), pk1.Get(), context, 6.36)
|
||||
}
|
||||
|
||||
hi := make([][2]*ring.Poly, parties)
|
||||
for i := uint64(0); i < parties; i++ {
|
||||
hi[i] = pcks[i].KeySwitch(ciphertexts[i].Value()[1])
|
||||
}
|
||||
|
||||
for i := uint64(0); i < parties; i++ {
|
||||
pcks[i].Aggregate(ciphertexts[i].Value(), hi)
|
||||
}
|
||||
|
||||
for i := uint64(0); i < parties; i++ {
|
||||
|
||||
verify_test_vectors(decryptor_sk1, coeffsWant, ciphertexts[i], t)
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func test_EKG_Protocol_Naive(parties uint64, sk []*ckks.SecretKey, collectivePk *ckks.PublicKey, ekgNaive []*EkgProtocolNaive) [][][][2]*ring.Poly {
|
||||
|
||||
// ROUND 0
|
||||
// Each party generates its samples
|
||||
samples := make([][][][2]*ring.Poly, parties)
|
||||
for i := uint64(0); i < parties; i++ {
|
||||
samples[i] = ekgNaive[i].GenSamples(sk[i].Get(), collectivePk.Get())
|
||||
}
|
||||
|
||||
// ROUND 1
|
||||
// Each party aggretates its sample with the other n-1 samples
|
||||
aggregatedSamples := make([][][][2]*ring.Poly, parties)
|
||||
for i := uint64(0); i < parties; i++ {
|
||||
aggregatedSamples[i] = ekgNaive[i].Aggregate(sk[i].Get(), collectivePk.Get(), samples)
|
||||
}
|
||||
|
||||
// ROUND 2
|
||||
// Each party aggregates sums its aggregatedSample with the other n-1 aggregated samples
|
||||
evk := make([][][][2]*ring.Poly, parties)
|
||||
for i := uint64(0); i < parties; i++ {
|
||||
evk[i] = ekgNaive[i].Finalize(aggregatedSamples)
|
||||
}
|
||||
|
||||
return evk
|
||||
}
|
||||
|
||||
func test_EKG_Protocol(parties uint64, ekgProtocols []*EkgProtocol, sk []*ckks.SecretKey, ephemeralKeys []*ring.Poly, crp [][][]*ring.Poly) [][][][2]*ring.Poly {
|
||||
|
||||
// ROUND 1
|
||||
samples := make([][][]*ring.Poly, parties)
|
||||
for i := uint64(0); i < parties; i++ {
|
||||
samples[i] = ekgProtocols[i].GenSamples(ephemeralKeys[i], sk[i].Get(), crp[i])
|
||||
}
|
||||
|
||||
//ROUND 2
|
||||
aggregatedSamples := make([][][][2]*ring.Poly, parties)
|
||||
for i := uint64(0); i < parties; i++ {
|
||||
aggregatedSamples[i] = ekgProtocols[i].Aggregate(sk[i].Get(), samples, crp[i])
|
||||
}
|
||||
|
||||
// ROUND 3
|
||||
keySwitched := make([][][]*ring.Poly, parties)
|
||||
sum := make([][][][2]*ring.Poly, parties)
|
||||
for i := uint64(0); i < parties; i++ {
|
||||
sum[i] = ekgProtocols[i].Sum(aggregatedSamples)
|
||||
keySwitched[i] = ekgProtocols[i].KeySwitch(ephemeralKeys[i], sk[i].Get(), sum[i])
|
||||
}
|
||||
|
||||
// ROUND 4
|
||||
collectiveEvaluationKey := make([][][][2]*ring.Poly, parties)
|
||||
for i := uint64(0); i < parties; i++ {
|
||||
collectiveEvaluationKey[i] = ekgProtocols[i].ComputeEVK(keySwitched, sum[i])
|
||||
}
|
||||
|
||||
return collectiveEvaluationKey
|
||||
}
|
||||
|
||||
func verify_test_vectors(decryptor *ckks.Decryptor, valuesWant []complex128, element ckks.CkksElement, t *testing.T) (err error) {
|
||||
|
||||
var plaintextTest *ckks.Plaintext
|
||||
var valuesTest []complex128
|
||||
|
||||
if element.Degree() == 0 {
|
||||
|
||||
plaintextTest = element.(*ckks.Plaintext)
|
||||
|
||||
} else {
|
||||
|
||||
if plaintextTest, err = decryptor.DecryptNew(element.(*ckks.Ciphertext)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
valuesTest = plaintextTest.DecodeComplex()
|
||||
|
||||
var DeltaReal0, DeltaImag0, DeltaReal1, DeltaImag1 float64
|
||||
|
||||
for i := range valuesWant {
|
||||
|
||||
// Test for big values (> 1)
|
||||
DeltaReal0 = real(valuesWant[i]) / real(valuesTest[i])
|
||||
DeltaImag0 = imag(valuesWant[i]) / imag(valuesTest[i])
|
||||
|
||||
// Test for small values (< 1)
|
||||
DeltaReal1 = real(valuesWant[i]) - real(valuesTest[i])
|
||||
DeltaImag1 = imag(valuesWant[i]) - imag(valuesTest[i])
|
||||
|
||||
if DeltaReal1 < 0 {
|
||||
DeltaReal1 *= -1
|
||||
}
|
||||
if DeltaImag1 < 0 {
|
||||
DeltaImag1 *= -1
|
||||
}
|
||||
|
||||
if (DeltaReal0 < 0.999 || DeltaReal0 > 1.001 || DeltaImag0 < 0.999 || DeltaImag0 > 1.001) && (DeltaReal1 > 0.001 || DeltaImag1 > 0.001) {
|
||||
t.Errorf("error : coeff %d, want %f have %f", i, valuesWant[i], valuesTest[i])
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
BIN
documentation/BFV.png
Normal file
BIN
documentation/BFV.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 371 KiB |
BIN
documentation/CKKS.png
Normal file
BIN
documentation/CKKS.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 244 KiB |
BIN
documentation/DBFV.png
Normal file
BIN
documentation/DBFV.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 122 KiB |
BIN
documentation/RING.png
Normal file
BIN
documentation/RING.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 268 KiB |
370
examples/bfv/examples_bfv.go
Normal file
370
examples/bfv/examples_bfv.go
Normal file
@@ -0,0 +1,370 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/lca1/lattigo/bfv"
|
||||
"github.com/lca1/lattigo/ring"
|
||||
"log"
|
||||
)
|
||||
|
||||
var N uint64
|
||||
var T uint64
|
||||
var Qi []uint64
|
||||
var Pi []uint64
|
||||
var Sigma float64
|
||||
var bfvContext *bfv.BfvContext
|
||||
|
||||
func Plaintext_Batching() {
|
||||
|
||||
N = 8
|
||||
T = 786433
|
||||
Qi = []uint64{1152921504053723137, 1152921504050839553}
|
||||
Pi = []uint64{576460752308273153, 576460752315482113, 576460752319021057}
|
||||
Sigma = float64(3.19)
|
||||
|
||||
bfvContext, err := bfv.NewBfvContextWithParam(N, T, Qi, Pi, Sigma)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
kgen := bfvContext.NewKeyGenerator()
|
||||
|
||||
Sk := kgen.NewSecretKey()
|
||||
|
||||
Decryptor, err := bfvContext.NewDecryptor(Sk, 3)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
pks := make([]*bfv.PublicKey, 5, 5)
|
||||
encryptors := make([]*bfv.Encryptor, 5, 5)
|
||||
for i := range pks {
|
||||
pks[i], err = kgen.NewPublicKey(Sk)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
encryptors[i], err = bfvContext.NewEncryptor(pks[i])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
evalKey, err := kgen.NewRelinKey(Sk, 2, 60)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
evaluator, err := bfvContext.NewEvaluator()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println("===========================================")
|
||||
fmt.Println("Homomorphic computations on batched integers")
|
||||
fmt.Println("===========================================")
|
||||
fmt.Println()
|
||||
fmt.Printf("Parameters : N=%d, T=%d, Qi = %dx60, sigma = %f \n", N, T, len(Qi), Sigma)
|
||||
fmt.Println()
|
||||
|
||||
maxvalue := uint64(880)
|
||||
|
||||
// (i, x, y)
|
||||
Driver1 := []int64{0, int64(ring.RandUniform(maxvalue)), int64(ring.RandUniform(maxvalue))}
|
||||
Driver2 := []int64{1, int64(ring.RandUniform(maxvalue)), int64(ring.RandUniform(maxvalue))}
|
||||
Driver3 := []int64{2, int64(ring.RandUniform(maxvalue)), int64(ring.RandUniform(maxvalue))}
|
||||
Driver4 := []int64{3, int64(ring.RandUniform(maxvalue)), int64(ring.RandUniform(maxvalue))}
|
||||
Rider := []int64{int64(ring.RandUniform(maxvalue)), int64(ring.RandUniform(maxvalue))}
|
||||
|
||||
coeffsD1 := []int64{0, 0, 0, 0, 0, 0, 0, 0}
|
||||
coeffsD2 := []int64{0, 0, 0, 0, 0, 0, 0, 0}
|
||||
coeffsD3 := []int64{0, 0, 0, 0, 0, 0, 0, 0}
|
||||
coeffsD4 := []int64{0, 0, 0, 0, 0, 0, 0, 0}
|
||||
coeffsR := []int64{0, 0, 0, 0, 0, 0, 0, 0}
|
||||
|
||||
coeffsD1[Driver1[0]<<1], coeffsD1[1+(Driver1[0])<<1] = Driver1[1], Driver1[2]
|
||||
coeffsD2[Driver2[0]<<1], coeffsD2[1+(Driver2[0])<<1] = Driver2[1], Driver2[2]
|
||||
coeffsD3[Driver3[0]<<1], coeffsD3[1+(Driver3[0])<<1] = Driver3[1], Driver3[2]
|
||||
coeffsD4[Driver4[0]<<1], coeffsD4[1+(Driver4[0])<<1] = Driver4[1], Driver4[2]
|
||||
|
||||
for i := uint64(0); i < N; i += 2 {
|
||||
coeffsR[i] = Rider[0]
|
||||
coeffsR[i+1] = Rider[1]
|
||||
}
|
||||
|
||||
mD1 := bfvContext.NewPlaintext()
|
||||
mD2 := bfvContext.NewPlaintext()
|
||||
mD3 := bfvContext.NewPlaintext()
|
||||
mD4 := bfvContext.NewPlaintext()
|
||||
mR := bfvContext.NewPlaintext()
|
||||
|
||||
batchEncoder := bfvContext.NewBatchEncoder()
|
||||
|
||||
batchEncoder.EncodeInt(coeffsD1, mD1)
|
||||
batchEncoder.EncodeInt(coeffsD2, mD2)
|
||||
batchEncoder.EncodeInt(coeffsD3, mD3)
|
||||
batchEncoder.EncodeInt(coeffsD4, mD4)
|
||||
batchEncoder.EncodeInt(coeffsR, mR)
|
||||
|
||||
fmt.Printf("Driver 1 : [")
|
||||
for i := uint64(0); i < N; i++ {
|
||||
fmt.Printf("%8d", coeffsD1[i])
|
||||
}
|
||||
fmt.Printf(" ] -> Encrypt with Pk1 -> CtD1\n")
|
||||
|
||||
fmt.Printf("Driver 2 : [")
|
||||
for i := uint64(0); i < N; i++ {
|
||||
fmt.Printf("%8d", coeffsD2[i])
|
||||
}
|
||||
|
||||
fmt.Printf(" ] -> Encrypt with Pk2 -> CtD2\n")
|
||||
|
||||
fmt.Printf("Driver 3 : [")
|
||||
for i := uint64(0); i < N; i++ {
|
||||
fmt.Printf("%8d", coeffsD3[i])
|
||||
}
|
||||
|
||||
fmt.Printf(" ] -> Encrypt with Pk3 -> CtD3\n")
|
||||
|
||||
fmt.Printf("Driver 4 : [")
|
||||
for i := uint64(0); i < N; i++ {
|
||||
fmt.Printf("%8d", coeffsD4[i])
|
||||
}
|
||||
|
||||
fmt.Printf(" ] -> Encrypt with Pk4 -> CtD4\n")
|
||||
|
||||
fmt.Printf("Rider : [")
|
||||
for i := uint64(0); i < N; i++ {
|
||||
fmt.Printf("%8d", coeffsR[i])
|
||||
}
|
||||
fmt.Printf(" ] -> Encrypt with Pk5 -> CtR\n")
|
||||
|
||||
fmt.Println()
|
||||
|
||||
CtD1, err := encryptors[0].EncryptNew(mD1)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
CtD2, err := encryptors[1].EncryptNew(mD2)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
CtD3, err := encryptors[2].EncryptNew(mD3)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
CtD4, err := encryptors[3].EncryptNew(mD4)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
CtR, err := encryptors[4].EncryptNew(mR)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println("Compute encrypted Distance = ((CtD1 + CtD2 + CtD3 + CtD4) - CtR)^2 ...")
|
||||
|
||||
if err := evaluator.Add(CtD1, CtD2, CtD1); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := evaluator.Add(CtD1, CtD3, CtD1); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := evaluator.Add(CtD1, CtD4, CtD1); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
evaluator.Sub(CtD1, CtR, CtD1)
|
||||
|
||||
CtD1 = evaluator.MulNew(CtD1, CtD1).(*bfv.Ciphertext)
|
||||
|
||||
CtD1, err = evaluator.RelinearizeNew(CtD1, evalKey)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println("Done!")
|
||||
fmt.Println()
|
||||
|
||||
mR, err = Decryptor.DecryptNew(CtD1)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
result, err := batchEncoder.DecodeUint(mR)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
r1, r1exp := result[0]+result[1], uint64((coeffsD1[0]-coeffsR[0])*(coeffsD1[0]-coeffsR[0])+(coeffsD1[1]-coeffsR[1])*(coeffsD1[1]-coeffsR[1]))
|
||||
r2, r2exp := result[2]+result[3], uint64((coeffsD2[2]-coeffsR[2])*(coeffsD2[2]-coeffsR[2])+(coeffsD2[3]-coeffsR[3])*(coeffsD2[3]-coeffsR[3]))
|
||||
r3, r3exp := result[4]+result[5], uint64((coeffsD3[4]-coeffsR[4])*(coeffsD3[4]-coeffsR[4])+(coeffsD3[5]-coeffsR[5])*(coeffsD3[5]-coeffsR[5]))
|
||||
r4, r4exp := result[6]+result[7], uint64((coeffsD4[6]-coeffsR[6])*(coeffsD4[6]-coeffsR[6])+(coeffsD4[7]-coeffsR[7])*(coeffsD4[7]-coeffsR[7]))
|
||||
|
||||
fmt.Printf("Distance with Driver %d : %6d = (%3d - %3d)^2 + (%3d - %3d)^2: %t \n", 1, r1, coeffsD1[0], coeffsR[0], coeffsD1[1], coeffsR[1], r1 == r1exp)
|
||||
fmt.Printf("Distance with Driver %d : %6d = (%3d - %3d)^2 + (%3d - %3d)^2: %t \n", 2, r2, coeffsD2[2], coeffsR[2], coeffsD2[3], coeffsR[3], r2 == r2exp)
|
||||
fmt.Printf("Distance with Driver %d : %6d = (%3d - %3d)^2 + (%3d - %3d)^2: %t \n", 3, r3, coeffsD3[4], coeffsR[4], coeffsD3[5], coeffsR[5], r3 == r3exp)
|
||||
fmt.Printf("Distance with Driver %d : %6d = (%3d - %3d)^2 + (%3d - %3d)^2: %t \n", 4, r4, coeffsD4[6], coeffsR[6], coeffsD4[7], coeffsR[7], r4 == r4exp)
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func Homomorphic_Inner_product() {
|
||||
|
||||
fmt.Println("===================================================================")
|
||||
fmt.Println("Homomorphic computations on batched integers (sum of inner product)")
|
||||
fmt.Println("===================================================================")
|
||||
fmt.Println()
|
||||
|
||||
N = 8192
|
||||
T = 281474976317441
|
||||
Qi = []uint64{1152921504066306049, 1152921504057917441, 1152921504053723137, 1152921504050839553}
|
||||
Pi = []uint64{576460752568975361, 576460752573431809, 576460752580902913, 576460752585490433, 576460752586407937}
|
||||
Sigma = float64(3.19)
|
||||
|
||||
fmt.Printf("Parameters : N=%d, T=%d, Qi = %dx60, sigma = %f \n", N, T, len(Qi), Sigma)
|
||||
fmt.Println()
|
||||
|
||||
bfvContext = bfv.NewBfvContext()
|
||||
if err := bfvContext.SetParameters(N, T, Qi, Pi, Sigma); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
kgen := bfvContext.NewKeyGenerator()
|
||||
|
||||
encoder := bfvContext.NewBatchEncoder()
|
||||
|
||||
Sk := kgen.NewSecretKey()
|
||||
|
||||
Pk, err := kgen.NewPublicKey(Sk)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
rlk, err := kgen.NewRelinKey(Sk, 2, 30)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
rotationKey, err := kgen.NewRotationKeysPow2(Sk, 30, true)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
Decryptor, err := bfvContext.NewDecryptor(Sk, 2)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
Encryptor, err := bfvContext.NewEncryptor(Pk)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
Evaluator, err := bfvContext.NewEvaluator()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println("Generating to random arrays (a and b) of 784 integers in the range [-500000, 500000]")
|
||||
fmt.Println()
|
||||
coeffsD1 := make([]int64, N)
|
||||
coeffsD2 := make([]int64, N)
|
||||
var tmp0, tmp1, sign1, sign2 int64
|
||||
|
||||
for i := int64(0); i < 784; i++ {
|
||||
tmp0 = int64(ring.RandUniform(2))
|
||||
tmp1 = int64(ring.RandUniform(2))
|
||||
sign1 = (tmp0 * -1) ^ (tmp0 ^ 1)
|
||||
sign2 = (tmp1 * -1) ^ (tmp1 ^ 1)
|
||||
coeffsD1[i] = int64(ring.RandUniform(500000)) * sign1
|
||||
coeffsD2[i] = int64(ring.RandUniform(500000)) * sign2
|
||||
}
|
||||
|
||||
fmt.Printf("a : [")
|
||||
for i := uint64(0); i < 8; i++ {
|
||||
fmt.Printf("%8d", coeffsD1[i])
|
||||
}
|
||||
fmt.Printf(" ... ]\n")
|
||||
|
||||
fmt.Printf("b : [")
|
||||
for i := uint64(0); i < 8; i++ {
|
||||
fmt.Printf("%8d", coeffsD2[i])
|
||||
}
|
||||
fmt.Printf(" ... ]\n")
|
||||
fmt.Println()
|
||||
|
||||
// Computes the sum of the inner product
|
||||
sum := int64(0)
|
||||
innerProduct := make([]int64, N)
|
||||
for i := uint64(0); i < 784; i++ {
|
||||
innerProduct[i] = coeffsD1[i] * coeffsD2[i]
|
||||
sum += innerProduct[i]
|
||||
}
|
||||
|
||||
// Creates two new plaintexts and encode the coeffs array on those plaintexts
|
||||
mD1 := bfvContext.NewPlaintext()
|
||||
if err := encoder.EncodeInt(coeffsD1, mD1); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
mD2 := bfvContext.NewPlaintext()
|
||||
if err := encoder.EncodeInt(coeffsD2, mD2); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Encrypts the first plaintext
|
||||
CtD1, err := Encryptor.EncryptNew(mD1)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Encrypts the second plaintext
|
||||
CtD2, err := Encryptor.EncryptNew(mD2)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println("Computation of the inner product followed by an inner sum...")
|
||||
// Inner product
|
||||
CtD1 = Evaluator.MulNew(CtD1, CtD2).(*bfv.Ciphertext)
|
||||
|
||||
CtD1, err = Evaluator.RelinearizeNew(CtD1, rlk)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Inner Sum
|
||||
if err := Evaluator.InnerSum(CtD1, rotationKey, CtD1); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println("Done!")
|
||||
fmt.Println()
|
||||
|
||||
// Decrypts
|
||||
mR, err := Decryptor.DecryptNew(CtD1)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Printf("Decrypt and decode : [")
|
||||
|
||||
coeffs, err := encoder.DecodeInt(mR)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for i := uint64(0); i < 4; i++ {
|
||||
fmt.Printf("%16d", coeffs[i])
|
||||
}
|
||||
fmt.Printf(" ... ]\n")
|
||||
fmt.Println("Inner sum of inner product :", coeffs[0], "==", sum, coeffs[0] == sum)
|
||||
|
||||
}
|
||||
|
||||
func main() {
|
||||
Plaintext_Batching()
|
||||
//Homomorphic_Inner_product()
|
||||
}
|
||||
137
examples/ckks/examples_ckks.go
Normal file
137
examples/ckks/examples_ckks.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/lca1/lattigo/ckks"
|
||||
"log"
|
||||
"math"
|
||||
"math/cmplx"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
func randomFloat(min, max float64) float64 {
|
||||
return min + rand.Float64()*(max-min)
|
||||
}
|
||||
|
||||
func randomComplex(min, max float64) complex128 {
|
||||
return complex(randomFloat(min, max), randomFloat(min, max))
|
||||
}
|
||||
|
||||
func chebyshevinterpolation() {
|
||||
var err error
|
||||
var logN, logQ, levels, scale uint64
|
||||
|
||||
// Scheme params
|
||||
logN = 14
|
||||
logQ = 40
|
||||
levels = 8
|
||||
scale = logQ
|
||||
sigma := 3.19
|
||||
|
||||
// Context
|
||||
var ckkscontext *ckks.CkksContext
|
||||
if ckkscontext, err = ckks.NewCkksContext(logN, logQ, scale, levels, sigma); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
kgen := ckkscontext.NewKeyGenerator()
|
||||
|
||||
// Keys
|
||||
var sk *ckks.SecretKey
|
||||
var pk *ckks.PublicKey
|
||||
if sk, pk, err = kgen.NewKeyPair(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Relinearization key
|
||||
var rlk *ckks.EvaluationKey
|
||||
if rlk, err = kgen.NewRelinKey(sk, logQ); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Encryptor
|
||||
var encryptor *ckks.Encryptor
|
||||
if encryptor, err = ckkscontext.NewEncryptor(pk); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Decryptor
|
||||
var decryptor *ckks.Decryptor
|
||||
if decryptor, err = ckkscontext.NewDecryptor(sk); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
//Evaluator
|
||||
var evaluator *ckks.Evaluator
|
||||
if evaluator = ckkscontext.NewEvaluator(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Values to encrypt
|
||||
values := make([]complex128, 1<<(logN-1))
|
||||
for i := range values {
|
||||
values[i] = complex(randomFloat(-1, 1), randomFloat(-0.1, 0.1))
|
||||
}
|
||||
|
||||
fmt.Printf("HEAAN parameters : logN = %d, logQ = %d, levels = %d (%d bits), logPrecision = %d, logScale = %d, sigma = %f \n", logN, logQ, levels, 60+(levels-1)*logQ, ckkscontext.Precision(), scale, sigma)
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("Values : %6f %6f %6f %6f...\n", round(values[0]), round(values[1]), round(values[2]), round(values[3]))
|
||||
fmt.Println()
|
||||
|
||||
// Plaintext creation and encoding process
|
||||
plaintext := ckkscontext.NewPlaintext(levels-1, scale)
|
||||
if err = plaintext.EncodeComplex(values); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Encryption process
|
||||
var ciphertext *ckks.Ciphertext
|
||||
if ciphertext, err = encryptor.EncryptNew(plaintext); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println("Evaluation of the function cos(exp(2*pi*i*x)) in the range [-1, 1] (degree of approximation : 65)")
|
||||
// Evaluation process
|
||||
chebyapproximation := ckks.Approximate(f, -1, 1, 65)
|
||||
if ciphertext, err = evaluator.EvaluateCheby(ciphertext, chebyapproximation, rlk); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println("Done... Consumed levels :", levels-1-ciphertext.Level())
|
||||
|
||||
// Decryption process
|
||||
if plaintext, err = decryptor.DecryptNew(ciphertext); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Decoding process
|
||||
valuesTest := plaintext.DecodeComplex()
|
||||
|
||||
// Computation of the reference values
|
||||
for i := range values {
|
||||
values[i] = f(values[i])
|
||||
}
|
||||
|
||||
// Prints results to compare
|
||||
fmt.Println()
|
||||
fmt.Printf("ValuesTest : %6f %6f %6f %6f...\n", round(valuesTest[0]), round(valuesTest[1]), round(valuesTest[2]), round(valuesTest[3]))
|
||||
fmt.Printf("ValuesWant : %6f %6f %6f %6f...\n", round(values[0]), round(values[1]), round(values[2]), round(values[3]))
|
||||
|
||||
}
|
||||
|
||||
func f(x complex128) complex128 {
|
||||
return cmplx.Cos(cmplx.Exp(2 * 3.141592653589793 * complex(0, 1) * x)) // cos(exp(2*pi*i*x))
|
||||
//return cmplx.Exp(x)/(cmplx.Exp(x) + 1) // sigmoid function
|
||||
}
|
||||
|
||||
func round(x complex128) complex128 {
|
||||
var factor float64
|
||||
factor = 100000000
|
||||
a := math.Round(real(x)*factor) / factor
|
||||
b := math.Round(imag(x)*factor) / factor
|
||||
return complex(a, b)
|
||||
}
|
||||
|
||||
func main() {
|
||||
chebyshevinterpolation()
|
||||
}
|
||||
8
go.mod
Normal file
8
go.mod
Normal file
@@ -0,0 +1,8 @@
|
||||
module github.com/lca1/lattigo
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422 // indirect
|
||||
)
|
||||
13
go.sum
Normal file
13
go.sum
Normal file
@@ -0,0 +1,13 @@
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422 h1:QzoH/1pFpZguR8NrRHLcO6jKqfv2zpuSqZLgdm7ZmjI=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd h1:/e+gpKk9r3dJobndpTytxS2gOy6m5uvpg+ISQoEcusQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
234
ring/float128.go
Normal file
234
ring/float128.go
Normal file
@@ -0,0 +1,234 @@
|
||||
// ==== Initial package ====
|
||||
// This package implements 128-bit ("double double") floating point using
|
||||
// a pair of 64-bit hardware floating point values and standard hardware
|
||||
// floating point operations. It is based directly on libqd by Yozo Hida,
|
||||
// Xiaoye S. Li, David H. Bailey, Yves Renard and E. Jason Riedy. Source:
|
||||
// http://crd.lbl.gov/~dhbailey/mpdist/qd-2.3.13.tar.gz
|
||||
// ==== Current package ====
|
||||
// It has been modified and adapted its required use in the BFV. Unused
|
||||
// functions have been removed, and some functions simplified to save
|
||||
// computation (full precision is not needed).
|
||||
package ring
|
||||
|
||||
// TODO : implement e^n from https://golang.org/src/math/exp.go?s=368:395#L4 and https://golang.org/src/math/ldexp.go
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
// A Float128 represents a double-double floating point number with
|
||||
// 106 bits of mantissa, or about 32 decimal digits. The zero value
|
||||
// for a Float128 represents the value 0.
|
||||
type Float128 [2]float64 // float128 represented by two float64s
|
||||
|
||||
//
|
||||
// SET/GET
|
||||
//
|
||||
|
||||
// Set 128-bit floating point object to 0.0
|
||||
func Float128SetZero() (result Float128) {
|
||||
result[0] = 0.0
|
||||
result[1] = 0.0
|
||||
return
|
||||
}
|
||||
|
||||
// Set 128-bit floating point object from an uint64.
|
||||
// To be used only if the integer is 53 or less bits
|
||||
// and if the computation will stay under 54 bits.
|
||||
func Float128SetUint53(i uint64) (result Float128) {
|
||||
result[0] = float64(i)
|
||||
result[1] = 0.0
|
||||
return
|
||||
}
|
||||
|
||||
// Set 128-bit floating point object from uint64
|
||||
// Allows to import integers bigger than 53 bits and
|
||||
// do computation with result of up to 64 bits.
|
||||
func Float128SetUint64(i uint64) (result Float128) {
|
||||
result[0] = float64(i >> 12)
|
||||
result[1] = float64(i&0xfff) / float64(4096)
|
||||
return
|
||||
}
|
||||
|
||||
// Set 128-bit floating point object from uint64
|
||||
// Allows to import integers bigger than 53 bits and
|
||||
// do computation with result of up to 64 bits.
|
||||
func Float128SetInt64(i int64) (result Float128) {
|
||||
sign := i < 0
|
||||
if sign {
|
||||
i *= -1
|
||||
}
|
||||
result[0] = float64(uint64(i) >> 12)
|
||||
result[1] = float64(uint64(i)&0xfff) / float64(4096)
|
||||
|
||||
if sign {
|
||||
result[0] *= -1
|
||||
result[1] *= -1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Get the uint64 value from 128-bit floating point object
|
||||
// rounded down, to be used if the result of the computation
|
||||
// is under 53 bits.
|
||||
func Float128ToUint53(f Float128) uint64 {
|
||||
return uint64(f[0])
|
||||
}
|
||||
|
||||
// Reconstruct an uint64 integer from a Float128 that was imported with the Float128SetUint64() function.
|
||||
// Isolates the integer part from the floatting point part, then adds the rounded floating point
|
||||
// part to the integer part (this prevents occasional rounding errors when the second floating element is negative)
|
||||
func Float128ToUint64(f Float128) uint64 {
|
||||
return uint64(f[0]*4096) + uint64(math.Round((f[0]*4096)-float64(uint64(f[0]*4096))+f[1]*4096))
|
||||
}
|
||||
|
||||
//
|
||||
// ADDITION
|
||||
//
|
||||
|
||||
// Compute fl(a+b) and err(a+b).
|
||||
func twoSum(a, b float64) (s, err float64) {
|
||||
s = a + b
|
||||
bb := s - a
|
||||
err = (a - (s - bb)) + (b - bb)
|
||||
return
|
||||
}
|
||||
|
||||
// Compute fl(a+b) and err(a+b). Assumes |a| >= |b|.
|
||||
func quickTwoSum(a, b float64) (s, err float64) {
|
||||
s = a + b
|
||||
err = b - (s - a)
|
||||
return
|
||||
}
|
||||
|
||||
// Compute D = D + D
|
||||
func Float128Add(a, b Float128) (f Float128) {
|
||||
s1, s2 := twoSum(a[0], b[0])
|
||||
t1, t2 := twoSum(a[1], b[1])
|
||||
s2 += t1
|
||||
s1, s2 = quickTwoSum(s1, s2)
|
||||
s2 += t2
|
||||
f[0], f[1] = quickTwoSum(s1, s2)
|
||||
return
|
||||
}
|
||||
|
||||
//
|
||||
// SUBTRACTION
|
||||
//
|
||||
|
||||
// Compute fl(a-b) and err(a-b).
|
||||
func twoDiff(a, b float64) (s, err float64) {
|
||||
s = a - b
|
||||
bb := s - a
|
||||
err = (a - (s - bb)) - (b + bb)
|
||||
return
|
||||
}
|
||||
|
||||
// Compute D = D - D
|
||||
func Float128Sub(a, b Float128) (f Float128) {
|
||||
s1, s2 := twoDiff(a[0], b[0])
|
||||
t1, t2 := twoDiff(a[1], b[1])
|
||||
s2 += t1
|
||||
s1, s2 = quickTwoSum(s1, s2)
|
||||
s2 += t2
|
||||
f[0], f[1] = quickTwoSum(s1, s2)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
//
|
||||
// MULTIPLICATION
|
||||
//
|
||||
|
||||
// Compute high and lo words of a float64 value
|
||||
func split(a float64) (hi, lo float64) {
|
||||
temp := 134217729.0 * a
|
||||
hi = temp - (temp - a)
|
||||
lo = a - hi
|
||||
return
|
||||
}
|
||||
|
||||
// Compute fl(a*b) and err(a*b).
|
||||
func twoProd(a, b float64) (p, err float64) {
|
||||
p = a * b
|
||||
aHi, aLo := split(a)
|
||||
bHi, bLo := split(b)
|
||||
err = ((aHi*bHi - p) + aHi*bLo + aLo*bHi) + aLo*bLo
|
||||
return
|
||||
}
|
||||
|
||||
// Compute D = D * D
|
||||
func Float128Mul(a, b Float128) (f Float128) {
|
||||
p1, p2 := twoProd(a[0], b[0])
|
||||
p2 += a[0]*b[1] + a[1]*b[0]
|
||||
f[0], f[1] = quickTwoSum(p1, p2)
|
||||
return
|
||||
}
|
||||
|
||||
//
|
||||
// DIVISION
|
||||
//
|
||||
|
||||
// Compute D = D / D
|
||||
func Float128Div(a, b Float128) Float128 {
|
||||
|
||||
var q1, p1, p2, p3, p4, v1, v2, r, t0, t1 float64
|
||||
var f Float128
|
||||
|
||||
//var q2, q3, p3, p4, v3, v4, r1, r2 float64
|
||||
//var f2, f3, t2 Float128
|
||||
|
||||
q1 = a[0] / b[0] //0.3ns
|
||||
|
||||
// MUL ~10ns
|
||||
p1, p2 = twoProd(q1, b[0])
|
||||
p2 += q1 * b[1]
|
||||
t0 = p1 + p2
|
||||
t1 = p2 - (t0 - p1)
|
||||
//
|
||||
|
||||
// SUB ~10ns
|
||||
p3, p4 = twoDiff(a[0], t0)
|
||||
v1, v2 = twoDiff(a[1], t1)
|
||||
p4 += v1
|
||||
p3, p4 = quickTwoSum(p3, p4)
|
||||
p4 += v2
|
||||
|
||||
r = (p3 + p4) / b[0] //r1
|
||||
|
||||
// ======= Additional Precision ========
|
||||
|
||||
// Not required for its current usage, so it is removed to improve the speed
|
||||
|
||||
//r1[0], r1[1] = quickTwoSum(p3, p4)
|
||||
|
||||
// MUL - SUB
|
||||
//q2 = r1[0] / b[0] //0.3ns
|
||||
//f2[0], f2[1] = q2, 0
|
||||
//p1, p2 = twoProd(f2[0], b[0])
|
||||
//p2 += f2[0]*b[1] + f2[1] * b[0]
|
||||
//t2[0], t2[1] = quickTwoSum(p1, p2)
|
||||
|
||||
// SUB
|
||||
//r = Float128Sub(r, t2)
|
||||
//p3, p4 = twoDiff(r1[0], t2[0])
|
||||
//v3, v4 = twoDiff(r1[1], t2[1])
|
||||
//p4 += v3
|
||||
//p3, p4 = quickTwoSum(p3, p4)
|
||||
//p2 += v4
|
||||
//r2, _ = quickTwoSum(p3, p4)
|
||||
//
|
||||
|
||||
//q3 = r2/b[0]
|
||||
//f3[0], f3[1] = q3, 0
|
||||
|
||||
// ======= ================== ========
|
||||
|
||||
// quickTwoSum(q1, r)
|
||||
f[0] = q1 + r
|
||||
f[1] = r - (f[0] - q1)
|
||||
|
||||
//f = Float128Add(f, f3)
|
||||
|
||||
return f
|
||||
}
|
||||
49
ring/float128_test.go
Normal file
49
ring/float128_test.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package ring
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Benchmark_Float128_Add(b *testing.B) {
|
||||
|
||||
var x, y Float128
|
||||
x = Float128SetUint64(1)
|
||||
y = Float128SetUint64(3)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
Float128Add(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Float128_Sub(b *testing.B) {
|
||||
|
||||
var x, y Float128
|
||||
x = Float128SetUint64(1)
|
||||
y = Float128SetUint64(3)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
Float128Sub(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Float128_Mul(b *testing.B) {
|
||||
|
||||
var x, y Float128
|
||||
x = Float128SetUint64(1)
|
||||
y = Float128SetUint64(3)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
Float128Mul(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Float128_Div(b *testing.B) {
|
||||
|
||||
var x, y Float128
|
||||
x = Float128SetUint64(1)
|
||||
y = Float128SetUint64(3)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
Float128Div(x, y)
|
||||
}
|
||||
}
|
||||
235
ring/int.go
Normal file
235
ring/int.go
Normal file
@@ -0,0 +1,235 @@
|
||||
package ring
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// Int is a generic implementation of natural arithmetic on integers,
|
||||
// built using Go's built-in "math/big.Int"
|
||||
type Int struct {
|
||||
Value big.Int // Integer value, theoretically ranging from -infinite to +infinite
|
||||
}
|
||||
|
||||
// NewInt creates a new Int with a given int64 value.
|
||||
func NewInt(v int64) *Int {
|
||||
i := new(Int)
|
||||
i.Value.SetInt64(v)
|
||||
return i
|
||||
}
|
||||
|
||||
// NewInt creates a new Int with a given int64 value.
|
||||
func NewUint(v uint64) *Int {
|
||||
i := new(Int)
|
||||
i.Value.SetUint64(v)
|
||||
return i
|
||||
}
|
||||
|
||||
func Copy(v *Int) *Int {
|
||||
i := new(Int)
|
||||
i.Value.Set(&v.Value)
|
||||
return i
|
||||
}
|
||||
|
||||
func RandInt(max *Int) *Int {
|
||||
n, err := rand.Int(rand.Reader, &max.Value)
|
||||
if err != nil {
|
||||
panic("error : crypto/rand/bigint")
|
||||
}
|
||||
i := new(Int)
|
||||
i.Value = *n
|
||||
return i
|
||||
}
|
||||
|
||||
// NewIntFromString creates a new Int from a string.
|
||||
// A prefix of ``0x'' or ``0X'' selects base 16;
|
||||
// the ``0'' prefix selects base 8, and
|
||||
// a ``0b'' or ``0B'' prefix selects base 2.
|
||||
// Otherwise the selected base is 10.
|
||||
func NewIntFromString(s string) *Int {
|
||||
i := new(Int)
|
||||
i.Value.SetString(s, 0)
|
||||
return i
|
||||
}
|
||||
|
||||
func (i *Int) String() string {
|
||||
return i.Value.String()
|
||||
}
|
||||
|
||||
// SetInt sets Int i with value v
|
||||
func (i *Int) SetInt(v int64) {
|
||||
i.Value.SetInt64(v)
|
||||
}
|
||||
|
||||
// SetInt sets Int i with value v
|
||||
func (i *Int) SetUint(v uint64) {
|
||||
i.Value.SetUint64(v)
|
||||
}
|
||||
|
||||
// SetBigInt sets Int i with bigint.Int
|
||||
func (i *Int) SetBigInt(v *Int) {
|
||||
i.Value.Set(&v.Value)
|
||||
}
|
||||
|
||||
// SetString sets the value of i from a string
|
||||
// A prefix of ``0x'' or ``0X'' selects base 16;
|
||||
// the ``0'' prefix selects base 8, and
|
||||
// a ``0b'' or ``0B'' prefix selects base 2.
|
||||
// Otherwise the selected base is 10.
|
||||
func (i *Int) SetString(s string) {
|
||||
i.Value.SetString(s, 0)
|
||||
}
|
||||
|
||||
func (i *Int) IsPrime(n int) bool {
|
||||
return i.Value.ProbablyPrime(n)
|
||||
}
|
||||
|
||||
// Add sets the target i to a + b.
|
||||
func (i *Int) Add(a, b *Int) *Int {
|
||||
i.Value.Add(&a.Value, &b.Value)
|
||||
return i
|
||||
}
|
||||
|
||||
// Sub sets the target i to a - b.
|
||||
func (i *Int) Sub(a, b *Int) *Int {
|
||||
i.Value.Sub(&a.Value, &b.Value)
|
||||
return i
|
||||
}
|
||||
|
||||
// Mul sets the target i to a * b.
|
||||
func (i *Int) Mul(a, b *Int) *Int {
|
||||
i.Value.Mul(&a.Value, &b.Value)
|
||||
return i
|
||||
}
|
||||
|
||||
// Div sets the target i to floor(a / b), which is the closest integer to zero for a/b
|
||||
func (i *Int) Div(a, b *Int) *Int {
|
||||
i.Value.Quo(&a.Value, &b.Value)
|
||||
return i
|
||||
}
|
||||
|
||||
// DivRound sets the target i to the integer closest to a / b .
|
||||
func (i *Int) DivRound(a, b *Int) *Int {
|
||||
_a := NewInt(1)
|
||||
_a.SetBigInt(a)
|
||||
i.Value.Quo(&_a.Value, &b.Value)
|
||||
r := NewInt(1)
|
||||
r.Value.Rem(&_a.Value, &b.Value)
|
||||
r2 := NewInt(1).Mul(r, NewInt(2))
|
||||
if r2.Value.CmpAbs(&b.Value) != -1.0 {
|
||||
if _a.Value.Sign() == b.Value.Sign() {
|
||||
i.Add(i, NewInt(1))
|
||||
} else {
|
||||
i.Sub(i, NewInt(1))
|
||||
}
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// Exp sets the target i to a^b mod m
|
||||
func (i *Int) Exp(a, b, m *Int) *Int {
|
||||
i.Value.Exp(&a.Value, &b.Value, &m.Value)
|
||||
return i
|
||||
}
|
||||
|
||||
// Mod sets the target i to a mod m.
|
||||
func (i *Int) Mod(a, m *Int) *Int {
|
||||
i.Value.Mod(&a.Value, &m.Value)
|
||||
return i
|
||||
}
|
||||
|
||||
// Inv sets the target i to a^-1 mod m.
|
||||
func (i *Int) Inv(a, m *Int) *Int {
|
||||
i.Value.ModInverse(&a.Value, &m.Value)
|
||||
return i
|
||||
}
|
||||
|
||||
// Neg sets the target i to -a mod m.
|
||||
func (i *Int) Neg(a, m *Int) *Int {
|
||||
i.Value.Neg(&a.Value)
|
||||
i.Mod(i, m)
|
||||
return i
|
||||
}
|
||||
|
||||
// Lsh sets the target i to a << m.
|
||||
func (i *Int) Lsh(a *Int, m uint64) *Int {
|
||||
i.Value.Lsh(&a.Value, uint(m))
|
||||
return i
|
||||
}
|
||||
|
||||
// Rsh sets the target i to a >> m.
|
||||
func (i *Int) Rsh(a *Int, m uint64) *Int {
|
||||
i.Value.Rsh(&a.Value, uint(m))
|
||||
return i
|
||||
}
|
||||
|
||||
// And sets the target i to a & b.
|
||||
func (i *Int) And(a, b *Int) *Int {
|
||||
i.Value.And(&a.Value, &b.Value)
|
||||
return i
|
||||
}
|
||||
|
||||
// EqualTo judges if i and i2 have the same value.
|
||||
func (i *Int) EqualTo(i2 *Int) bool {
|
||||
r := i.Value.Cmp(&i2.Value)
|
||||
if r == 0 {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Cmp compares i and i2 and returns:
|
||||
//
|
||||
// -1 if i < i2
|
||||
// 0 if i == i2
|
||||
// +1 if i > i2
|
||||
//
|
||||
func (i *Int) Compare(i2 *Int) int {
|
||||
return i.Value.Cmp(&i2.Value)
|
||||
}
|
||||
|
||||
// Bits returns the bit stream and bit length of i's absolute value.
|
||||
// For example, 6=110, this function will return ([0, 1, 1], 3)
|
||||
func (i *Int) Bits() ([]uint, uint) {
|
||||
var z Int
|
||||
z.Value.Abs(&i.Value)
|
||||
n := z.Value.BitLen()
|
||||
bits := make([]uint, n)
|
||||
for j := 0; j < n; j++ {
|
||||
bits[j] = z.Value.Bit(j)
|
||||
}
|
||||
return bits, uint(n)
|
||||
}
|
||||
|
||||
// Uint32 returns the low 32 bits of i as uint32
|
||||
func (i *Int) Uint32() uint32 {
|
||||
return uint32(i.Value.Uint64())
|
||||
}
|
||||
|
||||
// Uint32 returns the low 32 bits of i as uint32
|
||||
func (i *Int) Uint64() uint64 {
|
||||
return i.Value.Uint64()
|
||||
}
|
||||
|
||||
// Int64 returns the low 64 bits of i as int64
|
||||
func (i *Int) Int64() int64 {
|
||||
return i.Value.Int64()
|
||||
}
|
||||
|
||||
// Int64 returns the low 64 bits of i as int64
|
||||
func (i *Int) Float64() float64 {
|
||||
return float64(i.Value.Int64())
|
||||
}
|
||||
|
||||
// center shifts r from [0, q) to (-q/2, q/2]
|
||||
func (i *Int) Center(Q *Int) *Int {
|
||||
qDiv2 := NewInt(1)
|
||||
qDiv2.Div(Q, NewInt(2))
|
||||
|
||||
if i.Compare(qDiv2) == 1.0 {
|
||||
i.Value.Sub(&i.Value, &Q.Value)
|
||||
}
|
||||
|
||||
return i
|
||||
}
|
||||
561
ring/int_test.go
Normal file
561
ring/int_test.go
Normal file
@@ -0,0 +1,561 @@
|
||||
package ring
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// test vectors for function Add
|
||||
type argAdd struct {
|
||||
x, y, want *Int
|
||||
}
|
||||
|
||||
var addVec = []argAdd{
|
||||
{NewInt(0), NewInt(0), NewInt(0)},
|
||||
{NewInt(0), NewInt(1), NewInt(1)},
|
||||
{NewInt(0), NewInt(-1), NewInt(-1)},
|
||||
{NewInt(1), NewInt(-1), NewInt(0)},
|
||||
{NewInt(123456789), NewInt(987654321), NewInt(1111111110)},
|
||||
{NewInt(-123456789), NewInt(987654321), NewInt(864197532)},
|
||||
{NewInt(-123456789), NewInt(-987654321), NewInt(-1111111110)},
|
||||
{NewInt(123456789), NewIntFromString("1234567899999999999999"), NewIntFromString("1234567900000123456788")},
|
||||
{NewInt(-123456789), NewIntFromString("1234567899999999999999"), NewIntFromString("1234567899999876543210")},
|
||||
{NewIntFromString("123456789123456789123456789123456789"), NewIntFromString("-123456789123456789123456789123456789"), NewInt(0)},
|
||||
{NewIntFromString("123456789123456789123456789123456789"), NewIntFromString("987654321987654321987654321987654321"), NewIntFromString("1111111111111111111111111111111111110")},
|
||||
}
|
||||
|
||||
func TestAdd(t *testing.T) {
|
||||
var z Int
|
||||
for i, testPair := range addVec {
|
||||
if !z.Add(testPair.x, testPair.y).EqualTo(testPair.want) {
|
||||
t.Errorf("Error Add test pair %v", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAdd(b *testing.B) {
|
||||
var z Int
|
||||
x := NewIntFromString("123456789")
|
||||
y := NewIntFromString("987654321")
|
||||
for i := 0; i < b.N; i++ {
|
||||
z.Add(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAddDebug(b *testing.B) {
|
||||
x := 123456789
|
||||
y := 987654321
|
||||
for i := 0; i < b.N; i++ {
|
||||
x = x + y
|
||||
}
|
||||
}
|
||||
|
||||
// test vectors for function Sub
|
||||
type argSub struct {
|
||||
x, y, want *Int
|
||||
}
|
||||
|
||||
var subVec = []argSub{
|
||||
{NewInt(0), NewInt(0), NewInt(0)},
|
||||
{NewInt(0), NewInt(1), NewInt(-1)},
|
||||
{NewInt(0), NewInt(-1), NewInt(1)},
|
||||
{NewInt(-1), NewInt(-1), NewInt(0)},
|
||||
{NewInt(123456789), NewInt(987654321), NewInt(-864197532)},
|
||||
{NewInt(-123456789), NewInt(987654321), NewInt(-1111111110)},
|
||||
{NewInt(-123456789), NewInt(-987654321), NewInt(864197532)},
|
||||
{NewInt(123456789), NewIntFromString("1234567899999999999999"), NewIntFromString("-1234567899999876543210")},
|
||||
{NewInt(-123456789), NewIntFromString("1234567899999999999999"), NewIntFromString("-1234567900000123456788")},
|
||||
{NewIntFromString("123456789123456789123456789123456789"), NewIntFromString("987654321987654321987654321987654321"), NewIntFromString("-864197532864197532864197532864197532")},
|
||||
{NewIntFromString("-123456789123456789123456789123456789"), NewIntFromString("-987654321987654321987654321987654321"), NewIntFromString("864197532864197532864197532864197532")},
|
||||
}
|
||||
|
||||
func TestSub(t *testing.T) {
|
||||
var z Int
|
||||
for i, testPair := range subVec {
|
||||
if !z.Sub(testPair.x, testPair.y).EqualTo(testPair.want) {
|
||||
t.Errorf("Error Sub test pair %v", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSub(b *testing.B) {
|
||||
x := NewIntFromString("123456789")
|
||||
y := NewIntFromString("987654321")
|
||||
for i := 0; i < b.N; i++ {
|
||||
x.Sub(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSubDebug(b *testing.B) {
|
||||
x := 123456789
|
||||
y := 987654321
|
||||
for i := 0; i < b.N; i++ {
|
||||
x = x - y
|
||||
}
|
||||
}
|
||||
|
||||
// test vectors for function Mul
|
||||
type argMul struct {
|
||||
x, y, want *Int
|
||||
}
|
||||
|
||||
var mulVec = []argMul{
|
||||
{NewInt(0), NewInt(0), NewInt(0)},
|
||||
{NewInt(1), NewInt(0), NewInt(0)},
|
||||
{NewInt(1), NewInt(-1), NewInt(-1)},
|
||||
{NewInt(-1), NewInt(-1), NewInt(1)},
|
||||
{NewInt(123456789), NewInt(987654321), NewInt(121932631112635269)},
|
||||
{NewInt(-123456789), NewInt(987654321), NewInt(-121932631112635269)},
|
||||
{NewInt(-123456789), NewInt(-987654321), NewInt(121932631112635269)},
|
||||
{NewInt(123456789), NewIntFromString("1234567899999999999999"), NewIntFromString("152415788736473099999876543211")},
|
||||
{NewInt(-123456789), NewIntFromString("1234567899999999999999"), NewIntFromString("-152415788736473099999876543211")},
|
||||
{NewIntFromString("123456789123456789123456789123456789"), NewIntFromString("-987654321987654321987654321987654321"), NewIntFromString("-121932631356500531591068431825636331816338969581771069347203169112635269")},
|
||||
{NewIntFromString("-123456789123456789123456789123456789"), NewIntFromString("-987654321987654321987654321987654321"), NewIntFromString("121932631356500531591068431825636331816338969581771069347203169112635269")},
|
||||
}
|
||||
|
||||
func TestMul(t *testing.T) {
|
||||
var z Int
|
||||
for i, testPair := range mulVec {
|
||||
if !z.Mul(testPair.x, testPair.y).EqualTo(testPair.want) {
|
||||
t.Errorf("Error Mul test pair %v", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMul(b *testing.B) {
|
||||
var z Int
|
||||
x := NewIntFromString("123456789")
|
||||
y := NewIntFromString("987654321")
|
||||
for i := 0; i < b.N; i++ {
|
||||
z.Mul(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMulDebug(b *testing.B) {
|
||||
y := int64(987654321)
|
||||
for i := 0; i < b.N; i++ {
|
||||
x := int64(123456789)
|
||||
x = int64(x * y)
|
||||
}
|
||||
}
|
||||
|
||||
// test vectors for function Div
|
||||
type argDiv struct {
|
||||
x, y, want *Int
|
||||
}
|
||||
|
||||
var divVec = []argDiv{
|
||||
{NewInt(0), NewInt(1), NewInt(0)},
|
||||
{NewInt(1), NewInt(2), NewInt(0)},
|
||||
{NewInt(5), NewInt(2), NewInt(2)},
|
||||
{NewInt(17), NewInt(-2), NewInt(-8)},
|
||||
{NewInt(987654321), NewInt(123456789), NewInt(8)},
|
||||
{NewInt(-987654320), NewInt(123456789), NewInt(-8)},
|
||||
{NewInt(-121932631112635269), NewInt(-987654321), NewInt(123456789)},
|
||||
{NewIntFromString("123456789123456789123456789123456789"), NewInt(123456789), NewIntFromString("1000000001000000001000000001")},
|
||||
{NewIntFromString("123456789123456789123456789123456789"), NewInt(-123456789), NewIntFromString("-1000000001000000001000000001")},
|
||||
{NewIntFromString("987654321987654321987654321987654321"), NewIntFromString("123456789123456789123456789123456789"), NewInt(8)},
|
||||
{NewIntFromString("-987654321987654321987654321987654321"), NewIntFromString("-123456789123456789123456789123456789"), NewInt(8)},
|
||||
}
|
||||
|
||||
func TestDiv(t *testing.T) {
|
||||
var z Int
|
||||
for i, testPair := range divVec {
|
||||
if !z.Div(testPair.x, testPair.y).EqualTo(testPair.want) {
|
||||
t.Errorf("Error Div test pair %v", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDiv(b *testing.B) {
|
||||
var z Int
|
||||
x := NewIntFromString("987654321")
|
||||
y := NewIntFromString("123456789")
|
||||
for i := 0; i < b.N; i++ {
|
||||
z.Div(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDivDebug(b *testing.B) {
|
||||
y := int64(123456789)
|
||||
for i := 0; i < b.N; i++ {
|
||||
x := int64(987654321)
|
||||
x = int64(math.Ceil(float64(x / y)))
|
||||
}
|
||||
}
|
||||
|
||||
// test vectors for function DivRound
|
||||
type argDivRound struct {
|
||||
x, y, want *Int
|
||||
}
|
||||
|
||||
var divRoundVec = []argDivRound{
|
||||
{NewInt(0), NewInt(1), NewInt(0)},
|
||||
{NewInt(1), NewInt(2), NewInt(1)},
|
||||
{NewInt(5), NewInt(2), NewInt(3)},
|
||||
{NewInt(5), NewInt(3), NewInt(2)},
|
||||
{NewInt(5), NewInt(-2), NewInt(-3)},
|
||||
{NewInt(-5), NewInt(2), NewInt(-3)},
|
||||
{NewInt(-5), NewInt(-2), NewInt(3)},
|
||||
{NewInt(987654321), NewInt(123456789), NewInt(8)},
|
||||
{NewInt(-987654320), NewInt(123456789), NewInt(-8)},
|
||||
{NewInt(-121932631112635269), NewInt(-987654321), NewInt(123456789)},
|
||||
{NewIntFromString("123456789123456789123456789123456789"), NewInt(123456789), NewIntFromString("1000000001000000001000000001")},
|
||||
{NewIntFromString("987654321987654321987654321987654321"), NewIntFromString("123456789123456789123456789123456789"), NewInt(8)},
|
||||
{NewIntFromString("-987654321987654321987654321987654321"), NewIntFromString("-123456789123456789123456789123456789"), NewInt(8)},
|
||||
}
|
||||
|
||||
func TestDivRound(t *testing.T) {
|
||||
var z Int
|
||||
for i, testPair := range divRoundVec {
|
||||
if !z.DivRound(testPair.x, testPair.y).EqualTo(testPair.want) {
|
||||
t.Errorf("Error DivRound test pair %v", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDivRound(b *testing.B) {
|
||||
var z Int
|
||||
x := NewIntFromString("987654321")
|
||||
y := NewIntFromString("123456789")
|
||||
for i := 0; i < b.N; i++ {
|
||||
z.DivRound(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDivRoundDebug(b *testing.B) {
|
||||
y := int64(123456789)
|
||||
for i := 0; i < b.N; i++ {
|
||||
x := int64(987654321)
|
||||
x = int64(math.Round(float64(x / y)))
|
||||
}
|
||||
}
|
||||
|
||||
// test vectors for function Exp
|
||||
type argExp struct {
|
||||
x, y, m, want *Int
|
||||
}
|
||||
|
||||
var expVec = []argExp{
|
||||
{NewInt(0), NewInt(1), NewInt(2), NewInt(0)},
|
||||
{NewInt(1), NewInt(0), NewInt(2), NewInt(1)},
|
||||
{NewInt(-1), NewInt(0), NewInt(2), NewInt(1)},
|
||||
{NewInt(123456789), NewInt(12345), NewInt(987654321), NewInt(658957095)},
|
||||
{NewInt(-123456789), NewInt(12345), NewInt(987654321), NewInt(328697226)},
|
||||
{NewInt(123456789), NewInt(12345), NewIntFromString("123456789123456789"), NewIntFromString("87718977473362236")},
|
||||
{NewInt(-123456789), NewInt(12345), NewIntFromString("123456789123456789"), NewIntFromString("35737811650094553")},
|
||||
{NewIntFromString("123456789123456789"), NewInt(12345), NewIntFromString("987654321987654321"), NewIntFromString("313081623313081623")},
|
||||
{NewIntFromString("-123456789123456789"), NewInt(12345), NewIntFromString("987654321987654321"), NewIntFromString("674572698674572698")},
|
||||
}
|
||||
|
||||
func TestExp(t *testing.T) {
|
||||
var z Int
|
||||
for i, testPair := range expVec {
|
||||
if !z.Exp(testPair.x, testPair.y, testPair.m).EqualTo(testPair.want) {
|
||||
t.Errorf("Error Exp test pair %v", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkExp(b *testing.B) {
|
||||
var z Int
|
||||
x := NewIntFromString("987654321")
|
||||
y := NewIntFromString("12345")
|
||||
m := NewIntFromString("6780883635459973527839456474")
|
||||
for i := 0; i < b.N; i++ {
|
||||
z.Exp(x, y, m)
|
||||
}
|
||||
}
|
||||
|
||||
// test vectors for function Mod
|
||||
type argMod struct {
|
||||
x, m, want *Int
|
||||
}
|
||||
|
||||
var modVec = []argMod{
|
||||
{NewInt(0), NewInt(1), NewInt(0)},
|
||||
{NewInt(1), NewInt(1), NewInt(0)},
|
||||
{NewInt(1), NewInt(2), NewInt(1)},
|
||||
{NewInt(5), NewInt(2), NewInt(1)},
|
||||
{NewInt(5), NewInt(3), NewInt(2)},
|
||||
{NewInt(-5), NewInt(2), NewInt(1)},
|
||||
{NewInt(-5), NewInt(4), NewInt(3)},
|
||||
{NewInt(987654321), NewInt(123456789), NewInt(9)},
|
||||
{NewInt(-987654321), NewInt(123456789), NewInt(123456780)},
|
||||
{NewIntFromString("123456789123456789123456789123456789"), NewInt(123456789), NewIntFromString("0")},
|
||||
{NewInt(123456789), NewIntFromString("123456789123456789123456789123456789"), NewIntFromString("123456789")},
|
||||
{NewIntFromString("123456789123456789123456789123456789"), NewInt(987654321), NewInt(246911409)},
|
||||
{NewIntFromString("98765432198765432198734567654567876789654321987654321"), NewIntFromString("123456789123456789123456789123456789"), NewIntFromString("41821061497654370731506248029506247")},
|
||||
{NewIntFromString("-98765432198765432198734567654567876789654321987654321"), NewIntFromString("123456789123456789123456789123456789"), NewIntFromString("81635727625802418391950541093950542")},
|
||||
}
|
||||
|
||||
func TestMod(t *testing.T) {
|
||||
var z Int
|
||||
for i, testPair := range modVec {
|
||||
if !z.Mod(testPair.x, testPair.m).EqualTo(testPair.want) {
|
||||
t.Errorf("Error Mod test pair %v", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMod(b *testing.B) {
|
||||
var z Int
|
||||
x := NewIntFromString("987654321")
|
||||
y := NewIntFromString("7681")
|
||||
for i := 0; i < b.N; i++ {
|
||||
z.Mod(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkModDebug(b *testing.B) {
|
||||
var x int64
|
||||
y := int64(7681)
|
||||
for i := 0; i < b.N; i++ {
|
||||
x = 987654321
|
||||
x = x % y
|
||||
}
|
||||
}
|
||||
|
||||
// test vectors for function Inv
|
||||
type argInv struct {
|
||||
x, m *Int
|
||||
}
|
||||
|
||||
var invVec = []argInv{
|
||||
{NewInt(1), NewInt(2)},
|
||||
{NewInt(-1), NewInt(2)},
|
||||
{NewInt(12345), NewInt(10001473)},
|
||||
{NewInt(123456789123456789), NewIntFromString("1152921504382476289")},
|
||||
{NewIntFromString("123456789123456789"), NewIntFromString("1152921504382476289")},
|
||||
{NewIntFromString("-123456789123456789"), NewIntFromString("1152921504382476289")},
|
||||
}
|
||||
|
||||
func TestInv(t *testing.T) {
|
||||
var z Int
|
||||
one := NewInt(1)
|
||||
for i, testPair := range invVec {
|
||||
z.Inv(testPair.x, testPair.m)
|
||||
z.Mul(&z, testPair.x)
|
||||
z.Mod(&z, testPair.m)
|
||||
if !z.EqualTo(one) {
|
||||
t.Errorf("Error Inv test pair %v", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkInv(b *testing.B) {
|
||||
var z Int
|
||||
x := NewIntFromString("123456789123456789")
|
||||
y := NewIntFromString("1152921504382476289")
|
||||
for i := 0; i < b.N; i++ {
|
||||
z.Inv(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
// test vectors for function Neg
|
||||
type argNeg struct {
|
||||
x, m *Int
|
||||
}
|
||||
|
||||
var negVec = []argNeg{
|
||||
{NewInt(1), NewInt(2)},
|
||||
{NewInt(-1), NewInt(2)},
|
||||
{NewInt(12345), NewInt(10001473)},
|
||||
{NewInt(123456789123456789), NewIntFromString("1152921504382476289")},
|
||||
{NewIntFromString("123456789123456789"), NewIntFromString("1152921504382476289")},
|
||||
{NewIntFromString("-123456789123456789"), NewIntFromString("1152921504382476289")},
|
||||
}
|
||||
|
||||
func TestNeg(t *testing.T) {
|
||||
var z Int
|
||||
zero := NewInt(0)
|
||||
for i, testPair := range negVec {
|
||||
z.Neg(testPair.x, testPair.m)
|
||||
z.Add(&z, testPair.x)
|
||||
z.Mod(&z, testPair.m)
|
||||
if !z.EqualTo(zero) {
|
||||
t.Errorf("Error Neg test pair %v", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNeg(b *testing.B) {
|
||||
var z Int
|
||||
x := NewIntFromString("123456789123456789")
|
||||
y := NewIntFromString("1152921504382476289")
|
||||
for i := 0; i < b.N; i++ {
|
||||
z.Neg(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
// test vectors for function Lsh
|
||||
type argLsh struct {
|
||||
x *Int
|
||||
m uint64
|
||||
want *Int
|
||||
}
|
||||
|
||||
var lshVec = []argLsh{
|
||||
{NewInt(1), 2, NewInt(4)},
|
||||
{NewInt(-1), 2, NewInt(-4)},
|
||||
{NewInt(12345), 10, NewInt(12641280)},
|
||||
{NewInt(123456789123456789), 10, NewIntFromString("126419752062419751936")},
|
||||
{NewIntFromString("123456789123456789"), 20, NewIntFromString("129453826111917825982464")},
|
||||
{NewIntFromString("-123456789123456789"), 20, NewIntFromString("-129453826111917825982464")},
|
||||
}
|
||||
|
||||
func TestLsh(t *testing.T) {
|
||||
var z Int
|
||||
for i, testPair := range lshVec {
|
||||
z.Lsh(testPair.x, testPair.m)
|
||||
if !z.EqualTo(testPair.want) {
|
||||
t.Errorf("Error Lsh test pair %v", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLsh(b *testing.B) {
|
||||
var z Int
|
||||
x := NewIntFromString("987654321")
|
||||
y := uint64(5)
|
||||
for i := 0; i < b.N; i++ {
|
||||
z.Lsh(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLshDebug(b *testing.B) {
|
||||
var x int64
|
||||
y := uint64(5)
|
||||
for i := 0; i < b.N; i++ {
|
||||
x = 987654321
|
||||
x = x << y
|
||||
}
|
||||
}
|
||||
|
||||
// test vectors for function Rsh
|
||||
type argRsh struct {
|
||||
x *Int
|
||||
m uint64
|
||||
want *Int
|
||||
}
|
||||
|
||||
var rshVec = []argRsh{
|
||||
{NewInt(1), 2, NewInt(0)},
|
||||
{NewInt(-1), 2, NewInt(-1)},
|
||||
{NewInt(12345), 10, NewInt(12)},
|
||||
{NewInt(123456789123456789), 10, NewIntFromString("120563270628375")},
|
||||
{NewIntFromString("123456789123456789"), 20, NewIntFromString("117737568973")},
|
||||
{NewIntFromString("-123456789123456789"), 20, NewIntFromString("-117737568974")},
|
||||
}
|
||||
|
||||
func TestRsh(t *testing.T) {
|
||||
var z Int
|
||||
for i, testPair := range rshVec {
|
||||
z.Rsh(testPair.x, testPair.m)
|
||||
if !z.EqualTo(testPair.want) {
|
||||
t.Errorf("Error Rsh test pair %v", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRsh(b *testing.B) {
|
||||
var z Int
|
||||
x := NewIntFromString("987654321")
|
||||
y := uint64(5)
|
||||
for i := 0; i < b.N; i++ {
|
||||
z.Rsh(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRshDebug(b *testing.B) {
|
||||
var x int64
|
||||
y := uint32(5)
|
||||
for i := 0; i < b.N; i++ {
|
||||
x = 987654321
|
||||
x = x >> y
|
||||
}
|
||||
}
|
||||
|
||||
// test vectors for function And
|
||||
type argAnd struct {
|
||||
x, y, want *Int
|
||||
}
|
||||
|
||||
var andVec = []argAnd{
|
||||
{NewInt(1), NewInt(2), NewInt(0)},
|
||||
{NewInt(-1), NewInt(2), NewInt(2)},
|
||||
{NewInt(-12345), NewInt(-54321), NewInt(-62521)},
|
||||
{NewInt(123456789123456789), NewInt(-987654321), NewIntFromString("123456788438718213")},
|
||||
{NewIntFromString("123456789123456789"), NewIntFromString("987654321987654321"), NewIntFromString("122892737510904337")},
|
||||
{NewIntFromString("-123456789123456789"), NewIntFromString("987654321987654321"), NewIntFromString("864761584476749985")},
|
||||
}
|
||||
|
||||
func TestAnd(t *testing.T) {
|
||||
var z Int
|
||||
for i, testPair := range andVec {
|
||||
z.And(testPair.x, testPair.y)
|
||||
if !z.EqualTo(testPair.want) {
|
||||
t.Errorf("Error And test pair %v", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAnd(b *testing.B) {
|
||||
var z Int
|
||||
x := NewIntFromString("123456789123456789")
|
||||
y := NewIntFromString("987654321987654321")
|
||||
for i := 0; i < b.N; i++ {
|
||||
z.And(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAndDebug(b *testing.B) {
|
||||
var x int64
|
||||
y := int64(123456789)
|
||||
for i := 0; i < b.N; i++ {
|
||||
x = 987654321
|
||||
x = x & y
|
||||
}
|
||||
}
|
||||
|
||||
// test vectors for function Bits
|
||||
type argBits struct {
|
||||
x *Int
|
||||
xBits []uint
|
||||
xBitLen uint
|
||||
}
|
||||
|
||||
var bitsVec = []argBits{
|
||||
{NewInt(0), []uint{0}, uint(0)},
|
||||
{NewInt(1), []uint{1}, uint(1)},
|
||||
{NewInt(-1), []uint{1}, uint(1)},
|
||||
{NewInt(2), []uint{0, 1}, uint(2)},
|
||||
{NewInt(-2), []uint{0, 1}, uint(2)},
|
||||
{NewInt(123456789), []uint{1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1}, uint(27)},
|
||||
{NewInt(-123456789), []uint{1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1}, uint(27)},
|
||||
{NewIntFromString("1152921504382476289"), []uint{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, uint(60)},
|
||||
{NewIntFromString("-1152921504382476289"), []uint{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, uint(60)},
|
||||
}
|
||||
|
||||
func TestBits(t *testing.T) {
|
||||
var zBits []uint
|
||||
var zBitLen uint
|
||||
for i, testPair := range bitsVec {
|
||||
zBits, zBitLen = testPair.x.Bits()
|
||||
if zBitLen != testPair.xBitLen {
|
||||
t.Errorf("Error Bits test pair %v", i)
|
||||
}
|
||||
for j := range zBits {
|
||||
if zBits[j] != testPair.xBits[j] {
|
||||
t.Errorf("Error Bits test pair %v", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkBits(b *testing.B) {
|
||||
x := NewIntFromString("123456789123456789123456789123456789")
|
||||
for i := 0; i < b.N; i++ {
|
||||
x.Bits()
|
||||
}
|
||||
}
|
||||
205
ring/modular_reduction.go
Normal file
205
ring/modular_reduction.go
Normal file
@@ -0,0 +1,205 @@
|
||||
package ring
|
||||
|
||||
import (
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
//============================
|
||||
//=== MONTGOMERY REDUCTION ===
|
||||
//============================
|
||||
|
||||
// MForm returns a*2^64 mod q.
|
||||
func MForm(a, q uint64, u []uint64) (r uint64) {
|
||||
mhi, _ := bits.Mul64(a, u[1])
|
||||
r = -(a*u[0] + mhi) * q
|
||||
if r >= q {
|
||||
r -= q
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MFormConstant is identical to MForm, except that it runs in constant time
|
||||
// and returns a value in [0, 2q-1]
|
||||
func MFormConstant(a, q uint64, u []uint64) (r uint64) {
|
||||
mhi, _ := bits.Mul64(a, u[1])
|
||||
r = -(a*u[0] + mhi) * q
|
||||
return
|
||||
}
|
||||
|
||||
// InvMForm returns a*(1/2^64) mod q.
|
||||
func InvMForm(a, q, qInv uint64) (r uint64) {
|
||||
r, _ = bits.Mul64(a*qInv, q)
|
||||
r = q - r
|
||||
if r >= q {
|
||||
r -= q
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// InvMFormConstan is indentical to InvMForm, except that it runs in constant time
|
||||
// and returns a value in [0, 2q-1]
|
||||
func InvMFormConstant(a, q, qInv uint64) (r uint64) {
|
||||
r, _ = bits.Mul64(a*qInv, q)
|
||||
r = q - r
|
||||
return
|
||||
}
|
||||
|
||||
// MRedParams computes the parameter qInv = (q^-1) mod 2^64,
|
||||
// required for MontgomeryReduce.
|
||||
func MRedParams(q uint64) (qInv uint64) {
|
||||
var x uint64
|
||||
qInv = 1
|
||||
x = q
|
||||
for i := 0; i < 63; i++ {
|
||||
qInv *= x
|
||||
qInv &= 0xFFFFFFFFFFFFFFFF
|
||||
x *= x
|
||||
x &= 0xFFFFFFFFFFFFFFFF
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MRed operates a 64x64 bit multiplication with
|
||||
// a montgomery reduction over a radix of 2^64.
|
||||
func MRed(x, y, q, qInv uint64) (r uint64) {
|
||||
ahi, alo := bits.Mul64(x, y)
|
||||
R := alo * qInv
|
||||
H, _ := bits.Mul64(R, q)
|
||||
r = ahi - H + q
|
||||
if r >= q {
|
||||
r -= q
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MRedConstant is identical to MRed except it runs in
|
||||
// constant time and returns a value in [0, 2q-1].
|
||||
func MRedConstant(x, y, q, qInv uint64) (r uint64) {
|
||||
ahi, alo := bits.Mul64(x, y)
|
||||
R := alo * qInv
|
||||
H, _ := bits.Mul64(R, q)
|
||||
r = ahi - H + q
|
||||
return
|
||||
}
|
||||
|
||||
//==========================
|
||||
//=== BARRETT REDUCTION ===
|
||||
//==========================
|
||||
|
||||
// BRedParams computes the parameters required for the Barret reduction with
|
||||
// a radix of 2^128.
|
||||
func BRedParams(q uint64) (params []uint64) {
|
||||
bigR := new(Int).Lsh(NewUint(1), 128)
|
||||
bigR.Div(bigR, NewUint(q))
|
||||
|
||||
// 2^radix // q
|
||||
mhi := new(Int).Rsh(bigR, 64).Uint64()
|
||||
mlo := bigR.Uint64()
|
||||
|
||||
return []uint64{mhi, mlo}
|
||||
}
|
||||
|
||||
// BRedAdd reduces a 64 bit integer by q.
|
||||
// Assumes that x <= 64bits.
|
||||
func BRedAdd(x, q uint64, u []uint64) (r uint64) {
|
||||
s0, _ := bits.Mul64(x, u[0])
|
||||
r = x - s0*q
|
||||
if r >= q {
|
||||
r -= q
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// BRedAddConstant is indentical to BReAdd, except it runs
|
||||
// in constant time and returns a value in [0, 2q-1]
|
||||
func BRedAddConstant(x, q uint64, u []uint64) uint64 {
|
||||
s0, _ := bits.Mul64(x, u[0])
|
||||
return x - s0*q
|
||||
}
|
||||
|
||||
// BRed operates a 64x64 bit multiplication with
|
||||
// a barrett reduction.
|
||||
func BRed(x, y, q uint64, u []uint64) (r uint64) {
|
||||
|
||||
var lhi, mhi, mlo, s0, s1, carry uint64
|
||||
|
||||
ahi, alo := bits.Mul64(x, y)
|
||||
|
||||
// (alo*ulo)>>64
|
||||
|
||||
lhi, _ = bits.Mul64(alo, u[1])
|
||||
|
||||
// ((ahi*ulo + alo*uhi) + (alo*ulo))>>64
|
||||
|
||||
mhi, mlo = bits.Mul64(alo, u[0])
|
||||
|
||||
s0, carry = bits.Add64(mlo, lhi, 0)
|
||||
|
||||
s1 = mhi + carry
|
||||
|
||||
mhi, mlo = bits.Mul64(ahi, u[1])
|
||||
|
||||
_, carry = bits.Add64(mlo, s0, 0)
|
||||
|
||||
lhi = mhi + carry
|
||||
|
||||
// (ahi*uhi) + (((ahi*ulo + alo*uhi) + (alo*ulo))>>64)
|
||||
|
||||
s0 = ahi*u[0] + s1 + lhi
|
||||
|
||||
r = alo - s0*q
|
||||
|
||||
if r >= q {
|
||||
r -= q
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// BRedConstant is indentical to BRed, except it runs
|
||||
// in constant time and returns a value in [0, 2q-1]
|
||||
func BRedConstant(x, y, q uint64, u []uint64) (r uint64) {
|
||||
|
||||
var lhi, mhi, mlo, s0, s1, carry uint64
|
||||
|
||||
ahi, alo := bits.Mul64(x, y)
|
||||
|
||||
// alo*ulo
|
||||
|
||||
lhi, _ = bits.Mul64(alo, u[1])
|
||||
|
||||
// ahi*ulo + alo*uhi
|
||||
|
||||
mhi, mlo = bits.Mul64(alo, u[0])
|
||||
|
||||
s0, carry = bits.Add64(mlo, lhi, 0)
|
||||
|
||||
s1 = mhi + carry
|
||||
|
||||
mhi, mlo = bits.Mul64(ahi, u[1])
|
||||
|
||||
_, carry = bits.Add64(mlo, s0, 0)
|
||||
|
||||
lhi = mhi + carry
|
||||
|
||||
// ahi*uhi
|
||||
|
||||
s0 = ahi*u[0] + s1 + lhi
|
||||
|
||||
r = alo - s0*q
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
//===============================
|
||||
//==== CONDITIONAL REDUCTION ====
|
||||
//===============================
|
||||
|
||||
// CRed reduce returns a mod q, where,
|
||||
// a is required to be in the range [0, 2q-1].
|
||||
func CRed(a, q uint64) uint64 {
|
||||
if a >= q {
|
||||
return a - q
|
||||
}
|
||||
return a
|
||||
}
|
||||
125
ring/ntt.go
Normal file
125
ring/ntt.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package ring
|
||||
|
||||
// NTT performes the NTT transformation on the coefficients a Polynomial,
|
||||
// based on the Context of the Polynomial.
|
||||
// https://arxiv.org/abs/1205.2926v2
|
||||
|
||||
// NTT performes the NTT transformation on the coefficients of a polynomial.
|
||||
func (context *Context) NTT(p1, p2 *Poly) {
|
||||
for x := range context.Modulus {
|
||||
NTT(p1.Coeffs[x], p2.Coeffs[x], context.N, context.nttPsi[x], context.Modulus[x], context.mredParams[x], context.bredParams[x])
|
||||
}
|
||||
}
|
||||
|
||||
// InvNTT performes the inverse NTT transformation on the coefficients of a polynomial,
|
||||
func (context *Context) InvNTT(p1, p2 *Poly) {
|
||||
for x := range context.Modulus {
|
||||
InvNTT(p1.Coeffs[x], p2.Coeffs[x], context.N, context.nttPsiInv[x], context.nttNInv[x], context.Modulus[x], context.mredParams[x])
|
||||
}
|
||||
}
|
||||
|
||||
func Butterfly(U, V, Psi, Q, Qinv uint64) (X, Y uint64) {
|
||||
if U > 2*Q {
|
||||
U -= 2 * Q
|
||||
}
|
||||
V = MRedConstant(V, Psi, Q, Qinv)
|
||||
X = U + V
|
||||
Y = U + 2*Q - V
|
||||
return
|
||||
}
|
||||
|
||||
func InvButterfly(U, V, Psi, Q, Qinv uint64) (X, Y uint64) {
|
||||
X = U + V
|
||||
if X > 2*Q {
|
||||
X -= 2 * Q
|
||||
}
|
||||
Y = MRedConstant(U+2*Q-V, Psi, Q, Qinv) // At the moment it is not possible to use MRedConstant if Q > 61 bits
|
||||
return
|
||||
}
|
||||
|
||||
func NTT(coeffs_in, coeffs_out []uint64, N uint64, nttPsi []uint64, Q, mredParams uint64, bredParams []uint64) {
|
||||
var j1, j2, t uint64
|
||||
var F uint64
|
||||
|
||||
// Copies the result of the first round of butterflies on p2 with approximate reduction
|
||||
t = N >> 1
|
||||
j2 = t - 1
|
||||
F = nttPsi[1]
|
||||
for j := uint64(0); j <= j2; j++ {
|
||||
coeffs_out[j], coeffs_out[j+t] = Butterfly(coeffs_in[j], coeffs_in[j+t], F, Q, mredParams)
|
||||
}
|
||||
|
||||
// Continues the rest of the second to the n-1 butterflies on p2 with approximate reduction
|
||||
for m := uint64(2); m < N; m <<= 1 {
|
||||
t >>= 1
|
||||
for i := uint64(0); i < m; i++ {
|
||||
|
||||
j1 = (i * t) << 1
|
||||
|
||||
j2 = j1 + t - 1
|
||||
|
||||
F = nttPsi[m+i]
|
||||
|
||||
for j := j1; j <= j2; j++ {
|
||||
coeffs_out[j], coeffs_out[j+t] = Butterfly(coeffs_out[j], coeffs_out[j+t], F, Q, mredParams)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finishes with an exact reduction
|
||||
for i := uint64(0); i < N; i++ {
|
||||
coeffs_out[i] = BRedAdd(coeffs_out[i], Q, bredParams)
|
||||
}
|
||||
}
|
||||
|
||||
func InvNTT(coeffs_in, coeffs_out []uint64, N uint64, nttPsiInv []uint64, nttNInv, Q, mredParams uint64) {
|
||||
|
||||
var j1, j2, h, t uint64
|
||||
var F uint64
|
||||
|
||||
// Copies the result of the first round of butterflies on p2 with approximate reduction
|
||||
t = 1
|
||||
j1 = 0
|
||||
h = N >> 1
|
||||
|
||||
for i := uint64(0); i < h; i++ {
|
||||
|
||||
j2 = j1
|
||||
|
||||
F = nttPsiInv[h+i]
|
||||
|
||||
for j := j1; j <= j2; j++ {
|
||||
coeffs_out[j], coeffs_out[j+t] = InvButterfly(coeffs_in[j], coeffs_in[j+t], F, Q, mredParams)
|
||||
}
|
||||
|
||||
j1 = j1 + (t << 1)
|
||||
}
|
||||
|
||||
// Continues the rest of the second to the n-1 butterflies on p2 with approximate reduction
|
||||
t <<= 1
|
||||
for m := N >> 1; m > 1; m >>= 1 {
|
||||
|
||||
j1 = 0
|
||||
h = m >> 1
|
||||
|
||||
for i := uint64(0); i < h; i++ {
|
||||
|
||||
j2 = j1 + t - 1
|
||||
|
||||
F = nttPsiInv[h+i]
|
||||
|
||||
for j := j1; j <= j2; j++ {
|
||||
coeffs_out[j], coeffs_out[j+t] = InvButterfly(coeffs_out[j], coeffs_out[j+t], F, Q, mredParams)
|
||||
}
|
||||
|
||||
j1 = j1 + (t << 1)
|
||||
}
|
||||
|
||||
t <<= 1
|
||||
}
|
||||
|
||||
// Finishes with an exact reduction given
|
||||
for j := uint64(0); j < N; j++ {
|
||||
coeffs_out[j] = MRed(coeffs_out[j], nttNInv, Q, mredParams)
|
||||
}
|
||||
}
|
||||
49
ring/primes.go
Normal file
49
ring/primes.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package ring
|
||||
|
||||
// First hundred (from 0x800000000000000 and upward) 60bit Primes allowing 65536 ǸTT
|
||||
var Pi60 = []uint64{576460752308273153, 576460752315482113, 576460752319021057, 576460752319414273, 576460752321642497,
|
||||
576460752325705729, 576460752328327169, 576460752329113601, 576460752329506817, 576460752329900033,
|
||||
576460752331210753, 576460752337502209, 576460752340123649, 576460752342876161, 576460752347201537,
|
||||
576460752347332609, 576460752352837633, 576460752354017281, 576460752355065857, 576460752355459073,
|
||||
576460752358604801, 576460752364240897, 576460752368435201, 576460752371187713, 576460752373547009,
|
||||
576460752374333441, 576460752376692737, 576460752378003457, 576460752378396673, 576460752380755969,
|
||||
576460752381411329, 576460752386129921, 576460752395173889, 576460752395960321, 576460752396091393,
|
||||
576460752396484609, 576460752399106049, 576460752405135361, 576460752405921793, 576460752409722881,
|
||||
576460752410116097, 576460752411033601, 576460752412082177, 576460752416145409, 576460752416931841,
|
||||
576460752421257217, 576460752427548673, 576460752429514753, 576460752435281921, 576460752437248001,
|
||||
576460752438558721, 576460752441966593, 576460752449044481, 576460752451141633, 576460752451534849,
|
||||
576460752462938113, 576460752465952769, 576460752468705281, 576460752469491713, 576460752472375297,
|
||||
576460752473948161, 576460752475389953, 576460752480894977, 576460752483254273, 576460752484827137,
|
||||
576460752486793217, 576460752486924289, 576460752492691457, 576460752498589697, 576460752498720769,
|
||||
576460752499507201, 576460752504225793, 576460752505405441, 576460752507240449, 576460752507764737,
|
||||
576460752509206529, 576460752510124033, 576460752510779393, 576460752511959041, 576460752514449409,
|
||||
576460752516284417, 576460752519168001, 576460752520347649, 576460752520609793, 576460752522969089,
|
||||
576460752523100161, 576460752524279809, 576460752525852673, 576460752526245889, 576460752526508033,
|
||||
576460752532013057, 576460752545120257, 576460752550100993, 576460752551804929, 576460752567402497,
|
||||
576460752568975361, 576460752573431809, 576460752580902913, 576460752585490433, 576460752586407937}
|
||||
|
||||
// Last hundred (from 0xfffffffffffffff and downward) 60bit Primes allowing 65536 ǸTT
|
||||
var Qi60 = []uint64{1152921504606584833, 1152921504598720513, 1152921504592429057, 1152921504581419009, 1152921504580894721,
|
||||
1152921504578273281, 1152921504577748993, 1152921504577486849, 1152921504568836097, 1152921504565166081,
|
||||
1152921504563331073, 1152921504556515329, 1152921504555466753, 1152921504554156033, 1152921504552583169,
|
||||
1152921504542883841, 1152921504538951681, 1152921504537378817, 1152921504531873793, 1152921504521650177,
|
||||
1152921504509853697, 1152921504508280833, 1152921504506970113, 1152921504495697921, 1152921504491241473,
|
||||
1152921504488620033, 1152921504479444993, 1152921504470794241, 1152921504468172801, 1152921504462929921,
|
||||
1152921504462667777, 1152921504455589889, 1152921504447987713, 1152921504442482689, 1152921504436191233,
|
||||
1152921504427278337, 1152921504419414017, 1152921504409190401, 1152921504403947521, 1152921504396869633,
|
||||
1152921504395821057, 1152921504373014529, 1152921504369344513, 1152921504368558081, 1152921504364625921,
|
||||
1152921504362790913, 1152921504361218049, 1152921504353615873, 1152921504337887233, 1152921504337625089,
|
||||
1152921504321372161, 1152921504314032129, 1152921504303022081, 1152921504301449217, 1152921504288342017,
|
||||
1152921504287293441, 1152921504286769153, 1152921504282836993, 1152921504274972673, 1152921504266321921,
|
||||
1152921504256622593, 1152921504253739009, 1152921504245088257, 1152921504241942529, 1152921504240107521,
|
||||
1152921504239583233, 1152921504238010369, 1152921504234078209, 1152921504231718913, 1152921504230670337,
|
||||
1152921504227524609, 1152921504214417409, 1152921504207339521, 1152921504205504513, 1152921504204193793,
|
||||
1152921504190824449, 1152921504179552257, 1152921504177192961, 1152921504176668673, 1152921504174309377,
|
||||
1152921504172474369, 1152921504164872193, 1152921504162512897, 1152921504139706369, 1152921504134987777,
|
||||
1152921504132628481, 1152921504122142721, 1152921504120832001, 1152921504116899841, 1152921504105627649,
|
||||
1152921504101957633, 1152921504100384769, 1152921504096452609, 1152921504093306881, 1152921504078364673,
|
||||
1152921504067092481, 1152921504066306049, 1152921504057917441, 1152921504053723137, 1152921504050839553}
|
||||
|
||||
var Q61u40N4096 = []uint64{0x20000000001f4001, 0x20000000001ae001, 0x200000000014e001, 0x200000000013c001, 0x2000000000112001, 0x2000000000104001, 0x20000000000e2001, 0x20000000000b0001, 0x200000000003a001, 0x200000000001a001, 0x1ffffffffffde001, 0x1ffffffffffce001, 0x1ffffffffffa4001, 0x1ffffffffff92001, 0x1ffffffffff7a001, 0x1ffffffffff74001, 0x1ffffffffff56001, 0x1ffffffffff0c001, 0x1ffffffffff02001, 0x1fffffffffec4001, 0x1fffffffffe96001, 0x1fffffffffe82001, 0x1fffffffffe5a001, 0x1fffffffffe10001, 0x1fffffffffe00001}
|
||||
var Q61u39N4096 = []uint64{0x20000000003aa001, 0x200000000038e001, 0x200000000036e001, 0x2000000000332001, 0x20000000002e4001, 0x20000000002de001, 0x2000000000262001, 0x2000000000208001, 0x20000000001f4001, 0x20000000001ae001, 0x200000000014e001, 0x200000000013c001, 0x2000000000112001, 0x2000000000104001, 0x20000000000e2001, 0x20000000000b0001, 0x200000000003a001, 0x200000000001a001, 0x1ffffffffffde001, 0x1ffffffffffce001, 0x1ffffffffffa4001, 0x1ffffffffff92001, 0x1ffffffffff7a001, 0x1ffffffffff74001, 0x1ffffffffff56001, 0x1ffffffffff0c001, 0x1ffffffffff02001, 0x1fffffffffec4001, 0x1fffffffffe96001, 0x1fffffffffe82001, 0x1fffffffffe5a001, 0x1fffffffffe10001, 0x1fffffffffe00001, 0x1fffffffffdd0001, 0x1fffffffffd08001, 0x1fffffffffcf8001, 0x1fffffffffc9e001, 0x1fffffffffc80001}
|
||||
var Q61u38N4096 = []uint64{0x20000000007c0001, 0x20000000007a6001, 0x200000000071c001, 0x2000000000718001, 0x20000000006b8001, 0x200000000069e001, 0x200000000069a001, 0x2000000000620001, 0x2000000000596001, 0x2000000000578001, 0x200000000052c001, 0x2000000000524001, 0x2000000000518001, 0x2000000000506001, 0x2000000000500001, 0x20000000004c4001, 0x20000000004be001, 0x2000000000460001, 0x2000000000454001, 0x2000000000436001, 0x20000000003aa001, 0x200000000038e001, 0x200000000036e001, 0x2000000000332001, 0x20000000002e4001, 0x20000000002de001, 0x2000000000262001, 0x2000000000208001, 0x20000000001f4001, 0x20000000001ae001, 0x200000000014e001, 0x200000000013c001, 0x2000000000112001, 0x2000000000104001, 0x20000000000e2001, 0x20000000000b0001, 0x200000000003a001, 0x200000000001a001, 0x1ffffffffffde001, 0x1ffffffffffce001, 0x1ffffffffffa4001, 0x1ffffffffff92001, 0x1ffffffffff7a001, 0x1ffffffffff74001, 0x1ffffffffff56001, 0x1ffffffffff0c001, 0x1ffffffffff02001, 0x1fffffffffec4001, 0x1fffffffffe96001, 0x1fffffffffe82001, 0x1fffffffffe5a001, 0x1fffffffffe10001, 0x1fffffffffe00001, 0x1fffffffffdd0001, 0x1fffffffffd08001, 0x1fffffffffcf8001, 0x1fffffffffc9e001, 0x1fffffffffc80001, 0x1fffffffffba6001, 0x1fffffffffb94001, 0x1fffffffffb76001, 0x1fffffffffb72001, 0x1fffffffffb54001, 0x1fffffffffb40001, 0x1ffffffffface001, 0x1fffffffffab0001, 0x1fffffffffa6e001, 0x1fffffffffa6a001, 0x1fffffffffa3e001, 0x1fffffffffa32001, 0x1fffffffffa2e001, 0x1fffffffffa10001, 0x1fffffffffa0a001, 0x1fffffffff9a2001, 0x1fffffffff998001, 0x1fffffffff978001, 0x1fffffffff932001, 0x1fffffffff8c6001, 0x1fffffffff8a8001, 0x1fffffffff89c001, 0x1fffffffff88e001, 0x1fffffffff842001}
|
||||
552
ring/ring.go
Normal file
552
ring/ring.go
Normal file
@@ -0,0 +1,552 @@
|
||||
package ring
|
||||
|
||||
import (
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// Applies the galois transform on a ring in NTT form
|
||||
// TODO : careful, not inplace!
|
||||
func PermuteNTT(polIn *Poly, gen uint64, polOut *Poly) {
|
||||
|
||||
var N, mask, logN, tmp, index uint64
|
||||
|
||||
N = uint64(len(polIn.Coeffs[0]))
|
||||
|
||||
logN = uint64(bits.Len64(N) - 1)
|
||||
|
||||
mask = (N << 1) - 1
|
||||
|
||||
for i := 0; i < len(polIn.Coeffs); i++ {
|
||||
|
||||
for j := uint64(0); j < N; j++ {
|
||||
|
||||
index = 2*bitReverse64(j, logN) + 1
|
||||
|
||||
tmp = ((gen * index & mask) - 1) >> 1
|
||||
|
||||
index = bitReverse64(tmp, logN)
|
||||
|
||||
polOut.Coeffs[i][j] = polIn.Coeffs[i][index]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Applies the galois transform on a ring
|
||||
// TODO : careful, not inplace!
|
||||
func (context *Context) Permute(polIn *Poly, gen uint64, polOut *Poly) {
|
||||
|
||||
var mask, index, indexRaw, logN uint64
|
||||
|
||||
mask = context.N - 1
|
||||
|
||||
logN = uint64(bits.Len64(mask))
|
||||
|
||||
for j, qi := range context.Modulus {
|
||||
|
||||
for i := uint64(0); i < context.N; i++ {
|
||||
|
||||
indexRaw = i * gen
|
||||
|
||||
index = indexRaw & mask
|
||||
|
||||
polOut.Coeffs[j][index] = polIn.Coeffs[j][i]
|
||||
|
||||
if (indexRaw>>logN)&1 == 1 {
|
||||
polOut.Coeffs[j][index] = qi - polOut.Coeffs[j][index]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (context *Context) MulByPow2New(p1 *Poly, pow2 uint64) (p2 *Poly) {
|
||||
p2 = context.NewPoly()
|
||||
context.MulByPow2(p1, pow2, p2)
|
||||
return
|
||||
}
|
||||
|
||||
func (context *Context) MulByPow2(p1 *Poly, pow2 uint64, p2 *Poly) {
|
||||
context.MForm(p1, p2)
|
||||
for i, Qi := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p2.Coeffs[i][j] = PowerOf2(p2.Coeffs[i][j], pow2, Qi, context.mredParams[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (context *Context) MultByMonomialNew(p1 *Poly, monomialDeg uint64) (p2 *Poly) {
|
||||
p2 = context.NewPoly()
|
||||
context.MultByMonomial(p1, monomialDeg, p2)
|
||||
return
|
||||
}
|
||||
|
||||
// Given p1 a ring in Z[x]/(X^n + 1), returns p1 * x^d
|
||||
func (context *Context) MultByMonomial(p1 *Poly, monomialDeg uint64, p2 *Poly) {
|
||||
|
||||
var shift uint64
|
||||
|
||||
shift = monomialDeg % (context.N << 1)
|
||||
|
||||
if shift == 0 {
|
||||
|
||||
for i := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
|
||||
p2.Coeffs[i][j] = p1.Coeffs[i][j]
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
tmpx := context.NewPoly()
|
||||
|
||||
if shift < context.N {
|
||||
|
||||
for i := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
|
||||
tmpx.Coeffs[i][j] = p1.Coeffs[i][j]
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
for i, qi := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
|
||||
tmpx.Coeffs[i][j] = qi - p1.Coeffs[i][j]
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shift %= context.N
|
||||
|
||||
for i, qi := range context.Modulus {
|
||||
for j := uint64(0); j < shift; j++ {
|
||||
|
||||
p2.Coeffs[i][j] = qi - tmpx.Coeffs[i][context.N-shift+j]
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for i := range context.Modulus {
|
||||
for j := shift; j < context.N; j++ {
|
||||
|
||||
p2.Coeffs[i][j] = tmpx.Coeffs[i][j-shift]
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Applies a bit reverse permutation on the ring
|
||||
func (context *Context) BitReverse(p1, p2 *Poly) {
|
||||
bitLenOfN := uint64(bits.Len64(context.N) - 1)
|
||||
|
||||
if p1 != p2 {
|
||||
for i := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p2.Coeffs[i][bitReverse64(j, bitLenOfN)] = p1.Coeffs[i][j]
|
||||
}
|
||||
}
|
||||
} else { // In place in case p1 = p2
|
||||
for x := range context.Modulus {
|
||||
for i := uint64(0); i < context.N; i++ {
|
||||
j := bitReverse64(i, bitLenOfN)
|
||||
if i < j {
|
||||
p2.Coeffs[x][i], p2.Coeffs[x][j] = p2.Coeffs[x][j], p2.Coeffs[x][i]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Applies a Galoi Automorphism on a ring in NTT form,
|
||||
// rotating the coefficients to the right by n.
|
||||
// Requirese the data to permuted in bitreversal order before
|
||||
// applying NTT.
|
||||
func (context *Context) Rotate(p1 *Poly, n uint64, p2 *Poly) {
|
||||
|
||||
var root, gal uint64
|
||||
|
||||
n &= (1 << context.N) - 1
|
||||
|
||||
for i, qi := range context.Modulus {
|
||||
|
||||
root = MRed(context.psiMont[i], context.psiMont[i], qi, context.mredParams[i])
|
||||
|
||||
root = modexpMontgomery(root, n, qi, context.mredParams[i], context.bredParams[i])
|
||||
|
||||
gal = MForm(1, qi, context.bredParams[i])
|
||||
|
||||
for j := uint64(1); j < context.N; j++ {
|
||||
|
||||
gal = MRed(gal, root, qi, context.mredParams[i])
|
||||
|
||||
p2.Coeffs[i][j] = MRed(p1.Coeffs[i][j], gal, qi, context.mredParams[i])
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ShiftL circulary shifts the coefficients of the ring p1 by n to the left
|
||||
func (context *Context) Shift(p1 *Poly, n uint64, p2 *Poly) {
|
||||
mask := uint64((1 << context.N) - 1)
|
||||
for i := range context.Modulus {
|
||||
p2.Coeffs[i] = append(p1.Coeffs[i][(n&mask):], p1.Coeffs[i][:(n&mask)]...)
|
||||
}
|
||||
}
|
||||
|
||||
// Sets a ring in montgomeryform
|
||||
func (context *Context) MForm(p1, p2 *Poly) {
|
||||
|
||||
for i, qi := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p2.Coeffs[i][j] = MForm(p1.Coeffs[i][j], qi, context.bredParams[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sets a ring in montgomeryform
|
||||
func (context *Context) InvMForm(p1, p2 *Poly) {
|
||||
|
||||
for i, qi := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p2.Coeffs[i][j] = InvMForm(p1.Coeffs[i][j], qi, context.mredParams[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adds two polynomials coefficient wise and applies a modular reduction
|
||||
func (context *Context) Add(p1, p2, p3 *Poly) {
|
||||
for i, qi := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p3.Coeffs[i][j] = CRed(p1.Coeffs[i][j]+p2.Coeffs[i][j], qi)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adds two polynomials coefficient wise without modular reduction
|
||||
// The output range will be [0,2*Qi -1]
|
||||
func (context *Context) AddNoMod(p1, p2, p3 *Poly) {
|
||||
for i := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p3.Coeffs[i][j] = p1.Coeffs[i][j] + p2.Coeffs[i][j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Subtract p2 to p1 coefficient wise and applies a modular reduction
|
||||
func (context *Context) Sub(p1, p2, p3 *Poly) {
|
||||
for i, qi := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p3.Coeffs[i][j] = CRed((p1.Coeffs[i][j]+qi)-p2.Coeffs[i][j], qi)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Subtract p2 to p1 coefficient wise without modular reduction
|
||||
// Since negative values are not allowed, the outupt range will be [0,2*Qi -1]
|
||||
func (context *Context) SubNoMod(p1, p2, p3 *Poly) {
|
||||
for i, qi := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p3.Coeffs[i][j] = (p1.Coeffs[i][j] + qi) - p2.Coeffs[i][j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set all coefficient of a ring to there additive inverse
|
||||
func (context *Context) Neg(p1, p2 *Poly) {
|
||||
for i, qi := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p2.Coeffs[i][j] = qi - p1.Coeffs[i][j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Modular reduction over the coefficients of the ring
|
||||
// Used when several operations can first be done safely
|
||||
// without modular reduction
|
||||
func (context *Context) Reduce(p1, p2 *Poly) {
|
||||
for i, qi := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p2.Coeffs[i][j] = BRedAdd(p1.Coeffs[i][j], qi, context.bredParams[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Applies a modular reduction to p1 by m
|
||||
func (context *Context) Mod(p1 *Poly, m uint64, p2 *Poly) {
|
||||
params := BRedParams(m)
|
||||
for i := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p2.Coeffs[i][j] = BRedAdd(p1.Coeffs[i][j], m, params)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Applies a modular reduction to p1 by m
|
||||
func (context *Context) AND(p1 *Poly, m uint64, p2 *Poly) {
|
||||
for i := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p2.Coeffs[i][j] = p1.Coeffs[i][j] & m
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Applies a modular reduction to p1 by m
|
||||
func (context *Context) OR(p1 *Poly, m uint64, p2 *Poly) {
|
||||
for i := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p2.Coeffs[i][j] = p1.Coeffs[i][j] | m
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Applies a modular reduction to p1 by m
|
||||
func (context *Context) XOR(p1 *Poly, m uint64, p2 *Poly) {
|
||||
for i := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p2.Coeffs[i][j] = p1.Coeffs[i][j] ^ m
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Multiplies p1 by p2 coefficient wise with a modular reduction
|
||||
func (context *Context) MulCoeffs(p1, p2, p3 *Poly) {
|
||||
for i, qi := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p3.Coeffs[i][j] = BRed(p1.Coeffs[i][j], p2.Coeffs[i][j], qi, context.bredParams[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Multiplies p1 by p2 coefficient wise with a modular reduction
|
||||
func (context *Context) MulCoeffsAndAdd(p1, p2, p3 *Poly) {
|
||||
for i, qi := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p3.Coeffs[i][j] = CRed(p3.Coeffs[i][j]+BRed(p1.Coeffs[i][j], p2.Coeffs[i][j], qi, context.bredParams[i]), qi)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Multiplies p1 by p2 coefficient wise with a modular reduction
|
||||
func (context *Context) MulCoeffsAndAddNoMod(p1, p2, p3 *Poly) {
|
||||
for i, qi := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p3.Coeffs[i][j] += BRed(p1.Coeffs[i][j], p2.Coeffs[i][j], qi, context.bredParams[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Multiplies p1 by p2 coefficient wise with a modular reduction
|
||||
func (context *Context) MulCoeffsMontgomery(p1, p2, p3 *Poly) {
|
||||
for i, qi := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p3.Coeffs[i][j] = MRed(p1.Coeffs[i][j], p2.Coeffs[i][j], qi, context.mredParams[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Multiplies p1 by p2 coefficient wise and adds the result to p3
|
||||
func (context *Context) MulCoeffsMontgomeryAndAdd(p1, p2, p3 *Poly) {
|
||||
for i, qi := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p3.Coeffs[i][j] = CRed(p3.Coeffs[i][j]+MRed(p1.Coeffs[i][j], p2.Coeffs[i][j], qi, context.mredParams[i]), qi)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Multiplies p1 by p2 coefficient wise and adds the result to p3
|
||||
func (context *Context) MulCoeffsMontgomeryAndAddNoMod(p1, p2, p3 *Poly) {
|
||||
for i, qi := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p3.Coeffs[i][j] += MRed(p1.Coeffs[i][j], p2.Coeffs[i][j], qi, context.mredParams[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Multiplies p1 by p2 coefficient wise and subss the result to p3
|
||||
func (context *Context) MulCoeffsMontgomeryAndSub(p1, p2, p3 *Poly) {
|
||||
for i, qi := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p3.Coeffs[i][j] = CRed(p3.Coeffs[i][j]+(qi-MRed(p1.Coeffs[i][j], p2.Coeffs[i][j], qi, context.mredParams[i])), qi)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Multiplies p1 by p2 coefficient wise and subss the result to p3
|
||||
func (context *Context) MulCoeffsMontgomeryAndSubNoMod(p1, p2, p3 *Poly) {
|
||||
for i, qi := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p3.Coeffs[i][j] = p3.Coeffs[i][j] + (qi - MRed(p1.Coeffs[i][j], p2.Coeffs[i][j], qi, context.mredParams[i]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MulcoeffsConstant multiplies p3 = p1 * p2
|
||||
// The output range of the modular reduction is [0, 2*Qi -1]
|
||||
func (context *Context) MulCoeffsConstant(p1, p2, p3 *Poly) {
|
||||
for i, qi := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p3.Coeffs[i][j] = BRedConstant(p1.Coeffs[i][j], p2.Coeffs[i][j], qi, context.bredParams[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MulcoeffsConstant multiplies p3 = p1 * p2
|
||||
// The output range of the modular reduction is [0, 2*Qi -1]
|
||||
func (context *Context) MulCoeffsConstantMontgomery(p1, p2, p3 *Poly) {
|
||||
for i, qi := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p3.Coeffs[i][j] = MRedConstant(p1.Coeffs[i][j], p2.Coeffs[i][j], qi, context.mredParams[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Multiplies p1 by p2
|
||||
func (context *Context) MulPoly(p1, p2, p3 *Poly) {
|
||||
|
||||
a := context.NewPoly()
|
||||
b := context.NewPoly()
|
||||
|
||||
context.NTT(p1, a)
|
||||
context.NTT(p2, b)
|
||||
context.MulCoeffs(a, b, p3)
|
||||
context.InvNTT(p3, p3)
|
||||
}
|
||||
|
||||
// Multiplies p1 by p2
|
||||
func (context *Context) MulPolyMontgomery(p1, p2, p3 *Poly) {
|
||||
|
||||
a := context.NewPoly()
|
||||
b := context.NewPoly()
|
||||
|
||||
context.NTT(p1, a)
|
||||
context.NTT(p2, b)
|
||||
context.MulCoeffsMontgomery(a, b, p3)
|
||||
context.InvNTT(p3, p3)
|
||||
}
|
||||
|
||||
// Multiplies p1 by p2 with naive convolution O(n^2)
|
||||
func (context *Context) MulPolyNaive(p1, p2, p3 *Poly) {
|
||||
|
||||
p1Copy := p1.CopyNew()
|
||||
p2Copy := p2.CopyNew()
|
||||
|
||||
context.MForm(p1Copy, p1Copy)
|
||||
|
||||
context.AND(p3, 0, p3)
|
||||
|
||||
for x, qi := range context.Modulus {
|
||||
|
||||
for i := uint64(0); i < context.N; i++ {
|
||||
|
||||
for j := uint64(0); j < i; j++ {
|
||||
p3.Coeffs[x][j] = CRed(p3.Coeffs[x][j]+(qi-MRed(p1Copy.Coeffs[x][i], p2Copy.Coeffs[x][context.N-i+j], qi, context.mredParams[x])), qi)
|
||||
}
|
||||
|
||||
for j := uint64(i); j < context.N; j++ {
|
||||
p3.Coeffs[x][j] = CRed(p3.Coeffs[x][j]+MRed(p1Copy.Coeffs[x][i], p2Copy.Coeffs[x][j-i], qi, context.mredParams[x]), qi)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Multiplies p1 by p2 with naive convolution O(n^2)
|
||||
func (context *Context) MulPolyNaiveMontgomery(p1, p2, p3 *Poly) {
|
||||
|
||||
p1Copy := p1.CopyNew()
|
||||
p2Copy := p2.CopyNew()
|
||||
|
||||
context.AND(p3, 0, p3)
|
||||
|
||||
for x, qi := range context.Modulus {
|
||||
|
||||
for i := uint64(0); i < context.N; i++ {
|
||||
|
||||
for j := uint64(0); j < i; j++ {
|
||||
p3.Coeffs[x][j] = CRed(p3.Coeffs[x][j]+(qi-MRed(p1Copy.Coeffs[x][i], p2Copy.Coeffs[x][context.N-i+j], qi, context.mredParams[x])), qi)
|
||||
}
|
||||
|
||||
for j := uint64(i); j < context.N; j++ {
|
||||
p3.Coeffs[x][j] = CRed(p3.Coeffs[x][j]+MRed(p1Copy.Coeffs[x][i], p2Copy.Coeffs[x][j-i], qi, context.mredParams[x]), qi)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exp raises p1 to p1^e
|
||||
// TODO : implement montgomery ladder
|
||||
func (context *Context) Exp(p1 *Poly, e uint64, p2 *Poly) {
|
||||
|
||||
context.NTT(p1, p1)
|
||||
|
||||
tmp := context.NewPoly()
|
||||
context.Add(tmp, p1, tmp)
|
||||
|
||||
for i := range context.Modulus {
|
||||
for x := uint64(0); x < context.N; x++ {
|
||||
p2.Coeffs[i][x] = 1
|
||||
}
|
||||
}
|
||||
|
||||
for i := e; i > 0; i >>= 1 {
|
||||
if (i & 1) == 1 {
|
||||
context.MulCoeffs(p2, tmp, p2)
|
||||
}
|
||||
context.MulCoeffs(tmp, p1, tmp)
|
||||
}
|
||||
|
||||
context.InvNTT(p2, p2)
|
||||
context.InvNTT(p1, p2)
|
||||
}
|
||||
|
||||
// Adds to each coefficients of p1 a scalar and applies a modular reduction
|
||||
func (context *Context) AddScalar(p1 *Poly, scalar uint64, p2 *Poly) {
|
||||
for i, Qi := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p2.Coeffs[i][j] = CRed(p1.Coeffs[i][j]+scalar, Qi)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Subbs to each coefficients of p1 by a scalar and applies a modular reduction
|
||||
func (context *Context) SubScalar(p1 *Poly, scalar uint64, p2 *Poly) {
|
||||
for i, Qi := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p2.Coeffs[i][j] = CRed(p1.Coeffs[i][j]+(Qi-scalar), Qi)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Multiplies each coefficients of p1 by a scalar and applies a modular reduction
|
||||
func (context *Context) MulScalar(p1 *Poly, scalar uint64, p2 *Poly) {
|
||||
var scalarMont uint64
|
||||
for i, Qi := range context.Modulus {
|
||||
scalarMont = MForm(BRedAdd(scalar, Qi, context.bredParams[i]), Qi, context.bredParams[i])
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p2.Coeffs[i][j] = MRed(p1.Coeffs[i][j], scalarMont, Qi, context.mredParams[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Multiplies each coefficients of p1 by a bigint variable and applies a modular reduction
|
||||
// To be used when the scalar is bigger than 32 bits
|
||||
func (context *Context) MulScalarBigint(p1 *Poly, scalar *Int, p2 *Poly) {
|
||||
|
||||
var QiB Int
|
||||
var coeff Int
|
||||
|
||||
for i, Qi := range context.Modulus {
|
||||
QiB.SetInt(int64(Qi))
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
coeff.SetUint(p1.Coeffs[i][j])
|
||||
coeff.Mul(&coeff, scalar)
|
||||
coeff.Mod(&coeff, &QiB)
|
||||
p2.Coeffs[i][j] = coeff.Uint64()
|
||||
}
|
||||
}
|
||||
}
|
||||
199
ring/ring_basis_extension.go
Normal file
199
ring/ring_basis_extension.go
Normal file
@@ -0,0 +1,199 @@
|
||||
package ring
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
//==========================================
|
||||
//===== CRT BASIS EXTENSION PARAMETERS =====
|
||||
//==========================================
|
||||
|
||||
//Algorithm from https://eprint.iacr.org/2018/117.pdf
|
||||
type BasisExtender struct {
|
||||
contextQ *Context
|
||||
contextP *Context
|
||||
|
||||
//Parameters for basis extension
|
||||
|
||||
// (Q/Qi)^-1) * r (mod each Qi) (in Montgomery form)
|
||||
qibMont []uint64
|
||||
// Q/qi (mod each Pj) (in Montgomery form)
|
||||
qispjMont [][]uint64
|
||||
// Q*v (mod each Pj) for v in [1,...,k] where k is the number of Pj modulies
|
||||
qpjInv [][]uint64
|
||||
// Qi as a float128 variable
|
||||
qiFloat128 []Float128
|
||||
}
|
||||
|
||||
func NewBasisExtender(contextQ, contextP *Context) (*BasisExtender, error) {
|
||||
|
||||
if contextQ.validated != true || contextP.validated != true {
|
||||
return nil, errors.New("error : both contexts need to be valiated before instantiating a new basis extender")
|
||||
}
|
||||
|
||||
newParams := new(BasisExtender)
|
||||
|
||||
var PjB Int
|
||||
var QiB Int
|
||||
var QiStar Int
|
||||
var QiBarre Int
|
||||
|
||||
newParams.contextQ = contextQ
|
||||
newParams.contextP = contextP
|
||||
|
||||
tmp := NewUint(1)
|
||||
|
||||
// Q, P in Bigint
|
||||
|
||||
newParams.qibMont = make([]uint64, len(contextQ.Modulus))
|
||||
newParams.qispjMont = make([][]uint64, len(contextQ.Modulus))
|
||||
newParams.qpjInv = make([][]uint64, len(contextP.Modulus))
|
||||
newParams.qiFloat128 = make([]Float128, len(contextQ.Modulus))
|
||||
|
||||
for i, qi := range contextQ.Modulus {
|
||||
|
||||
QiB.SetUint(qi)
|
||||
QiStar.Div(contextQ.ModulusBigint, &QiB)
|
||||
QiBarre.Inv(&QiStar, &QiB)
|
||||
QiBarre.Mod(&QiBarre, &QiB)
|
||||
|
||||
// (Q/Qi)^-1) * r (mod Qi) (in Montgomery form)
|
||||
newParams.qibMont[i] = MForm(QiBarre.Uint64(), qi, contextQ.bredParams[i])
|
||||
|
||||
// (Q/qi * r) (mod Pj) (in Montgomery form)
|
||||
newParams.qispjMont[i] = make([]uint64, len(contextP.Modulus))
|
||||
for j, pj := range contextP.Modulus {
|
||||
PjB.SetUint(pj)
|
||||
newParams.qispjMont[i][j] = MForm(tmp.Mod(&QiStar, &PjB).Uint64(), pj, contextP.bredParams[j])
|
||||
}
|
||||
|
||||
newParams.qiFloat128[i] = Float128SetUint64(qi)
|
||||
}
|
||||
|
||||
// Correction Term (v*Q) mod each Pj
|
||||
var v uint64
|
||||
for j, pj := range contextP.Modulus {
|
||||
// [Q]_{pi}
|
||||
PjB.SetUint(pj)
|
||||
v = pj - PjB.Mod(contextQ.ModulusBigint, &PjB).Uint64()
|
||||
newParams.qpjInv[j] = make([]uint64, len(contextQ.Modulus)+1)
|
||||
newParams.qpjInv[j][0] = 0
|
||||
for i := 1; i < len(contextQ.Modulus)+1; i++ {
|
||||
newParams.qpjInv[j][i] = CRed(newParams.qpjInv[j][i-1]+v, pj)
|
||||
}
|
||||
}
|
||||
|
||||
return newParams, nil
|
||||
}
|
||||
|
||||
// Extends the basis of a ring
|
||||
// Given a ring with coefficients in basis {Q0,Q1....Qi}
|
||||
// Extends its basis from {Q0,Q1....Qi} to {Q0,Q1....Qi,P0,P1...Pj}
|
||||
func (Parameters *BasisExtender) ExtendBasis(p1, p2 *Poly) {
|
||||
|
||||
var v uint64
|
||||
var vi, yiFloat128 Float128
|
||||
var xpj uint64
|
||||
|
||||
y := make([]uint64, len(Parameters.contextQ.Modulus))
|
||||
|
||||
// If the receiver is equal to p1, then extend the number of modulies of p1
|
||||
if p1 == p2 {
|
||||
coeffsNewBase := make([][]uint64, len(Parameters.contextP.Modulus))
|
||||
for i := 0; i < len(Parameters.contextP.Modulus); i++ {
|
||||
coeffsNewBase[i] = make([]uint64, Parameters.contextQ.N)
|
||||
}
|
||||
p1.Coeffs = append(p1.Coeffs, coeffsNewBase...)
|
||||
} else { // Else copies the qi coefficients of p1 on p2
|
||||
for i := range Parameters.contextQ.Modulus {
|
||||
for j := uint64(0); j < Parameters.contextQ.N; j++ {
|
||||
p2.Coeffs[i][j] = p1.Coeffs[i][j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//We loop over each coefficient and apply the basis extension
|
||||
for x := uint64(0); x < Parameters.contextQ.N; x++ {
|
||||
|
||||
vi[0], vi[1] = 0, 0
|
||||
|
||||
for i, qi := range Parameters.contextQ.Modulus {
|
||||
|
||||
y[i] = MRed(p1.Coeffs[i][x], Parameters.qibMont[i], qi, Parameters.contextQ.mredParams[i])
|
||||
|
||||
yiFloat128[0] = float64(y[i] >> 12)
|
||||
yiFloat128[1] = float64(y[i]&0xfff) / float64(4096)
|
||||
|
||||
yiFloat128 = Float128Div(yiFloat128, Parameters.qiFloat128[i])
|
||||
|
||||
vi = Float128Add(vi, yiFloat128)
|
||||
}
|
||||
|
||||
// Index of the correction term
|
||||
v = uint64(vi[0])
|
||||
|
||||
//For each Pi we sum over the Qi
|
||||
for j, pj := range Parameters.contextP.Modulus {
|
||||
xpj = 0
|
||||
|
||||
for i := range Parameters.contextQ.Modulus {
|
||||
xpj += MRed(y[i], Parameters.qispjMont[i][j], pj, Parameters.contextP.mredParams[j])
|
||||
|
||||
if i&7 == 6 { //Only every 7 addition, since we add one more 60 bit integer after the loop
|
||||
xpj = BRedAdd(xpj, pj, Parameters.contextP.bredParams[j])
|
||||
}
|
||||
}
|
||||
|
||||
p2.Coeffs[j+len(Parameters.contextQ.Modulus)][x] = BRedAdd(xpj+Parameters.qpjInv[j][v], pj, Parameters.contextP.bredParams[j])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Approximate basis extension (returns the value + some multiple of Q (equal to the correction term v) in the basis QP)
|
||||
// The algorithm is identical to the ExtendBasis, except it doesn't make use of the correction term v to remove the additional multiplie of Q
|
||||
// introduced during the basis extension.
|
||||
func (Parameters *BasisExtender) ExtendBasisApproximate(p1, p2 *Poly) {
|
||||
|
||||
var xpj uint64
|
||||
|
||||
y := make([]uint64, len(Parameters.contextQ.Modulus))
|
||||
|
||||
// If the receiver is equal to p1, then extend the number of modulies of p1
|
||||
if p1 == p2 {
|
||||
coeffsNewBase := make([][]uint64, len(Parameters.contextP.Modulus))
|
||||
for i := 0; i < len(Parameters.contextP.Modulus); i++ {
|
||||
coeffsNewBase[i] = make([]uint64, Parameters.contextQ.N)
|
||||
}
|
||||
p1.Coeffs = append(p1.Coeffs, coeffsNewBase...)
|
||||
} else { // Else copies the qi coefficients of p1 on p2
|
||||
for i := range Parameters.contextQ.Modulus {
|
||||
for j := uint64(0); j < Parameters.contextQ.N; j++ {
|
||||
p2.Coeffs[i][j] = p1.Coeffs[i][j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//We loop over each array of coeffs
|
||||
for x := uint64(0); x < Parameters.contextQ.N; x++ {
|
||||
|
||||
for i, qi := range Parameters.contextQ.Modulus {
|
||||
y[i] = MRed(p1.Coeffs[i][x], Parameters.qibMont[i], qi, Parameters.contextQ.mredParams[i])
|
||||
}
|
||||
|
||||
//For each Pi we sum over the Qi
|
||||
for j, pj := range Parameters.contextP.Modulus {
|
||||
xpj = 0
|
||||
|
||||
for i := range Parameters.contextQ.Modulus {
|
||||
|
||||
xpj += MRed(y[i], Parameters.qispjMont[i][j], pj, Parameters.contextP.mredParams[j])
|
||||
|
||||
if i&7 == 6 { //Only every 7 addition, since we add one more 60 bit integer after the loop
|
||||
xpj = BRedAdd(xpj, pj, Parameters.contextP.bredParams[j])
|
||||
}
|
||||
}
|
||||
|
||||
p2.Coeffs[j+len(Parameters.contextQ.Modulus)][x] = BRedAdd(xpj, pj, Parameters.contextP.bredParams[j])
|
||||
}
|
||||
}
|
||||
}
|
||||
431
ring/ring_benchmark_test.go
Normal file
431
ring/ring_benchmark_test.go
Normal file
@@ -0,0 +1,431 @@
|
||||
package ring
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Benchmark_POLYNOMIAL(b *testing.B) {
|
||||
|
||||
for i := uint64(0); i < 1; i++ {
|
||||
|
||||
N := uint64(2 << (12 + i))
|
||||
T := uint64(65537)
|
||||
|
||||
Qi := Qi60[uint64(len(Qi60))-2<<i:]
|
||||
Pi := Pi60[uint64(len(Pi60))-((2<<i)+1):]
|
||||
|
||||
sigma := 3.19
|
||||
|
||||
contextT := NewContext()
|
||||
contextT.SetParameters(N, []uint64{T})
|
||||
contextT.ValidateParameters()
|
||||
|
||||
contextQ := NewContext()
|
||||
contextQ.SetParameters(N, Qi)
|
||||
contextQ.ValidateParameters()
|
||||
|
||||
contextP := NewContext()
|
||||
contextP.SetParameters(N, Pi)
|
||||
contextP.ValidateParameters()
|
||||
|
||||
contextQP := NewContext()
|
||||
contextQP.Merge(contextQ, contextP)
|
||||
|
||||
benchmark_GaussPoly(sigma, contextQ, b)
|
||||
|
||||
benchmark_KYSGaussPoly(sigma, contextQ, b)
|
||||
|
||||
benchmark_TernaryPoly(contextQ, b)
|
||||
|
||||
benchmark_UniformPoly(contextQ, b)
|
||||
|
||||
benchmark_NTT(contextQ, b)
|
||||
|
||||
benchmark_InvNTT(contextQ, b)
|
||||
|
||||
benchmark_MulScalar(contextQ, b)
|
||||
|
||||
benchmark_Neg(contextQ, b)
|
||||
|
||||
benchmark_Sub(contextQ, b)
|
||||
|
||||
benchmark_Add(contextQ, b)
|
||||
|
||||
benchmark_MulCoeffs(contextQ, b)
|
||||
|
||||
benchmark_MulCoeffsMontgomery(contextQ, b)
|
||||
|
||||
benchmark_MulPoly(contextQ, b)
|
||||
|
||||
benchmark_MulPolyMontgomery(contextQ, b)
|
||||
|
||||
benchmark_MulPolyNaiveMontgomery(contextQ, b)
|
||||
|
||||
benchmark_ExtendBasis(contextQ, contextP, contextQP, b)
|
||||
|
||||
benchmark_SimpleScaling(T, contextQ, b)
|
||||
|
||||
benchmark_ComplexScaling(T, contextQ, contextP, contextQP, b)
|
||||
|
||||
benchmark_Marshaler(contextQ, b)
|
||||
|
||||
benchmark_UnMarshaler(contextQ, b)
|
||||
|
||||
benchmark_BRed(b)
|
||||
|
||||
benchmark_BRedAdd(b)
|
||||
|
||||
benchmark_MRed(b)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func benchmark_UnMarshaler(context *Context, b *testing.B) {
|
||||
|
||||
p := context.NewUniformPoly()
|
||||
b.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/MarshalBinary", context.N, len(context.Modulus), 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
p.MarshalBinary()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func benchmark_Marshaler(context *Context, b *testing.B) {
|
||||
|
||||
p := context.NewUniformPoly()
|
||||
|
||||
data, _ := p.MarshalBinary()
|
||||
b.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/UnMarshalBinary", context.N, len(context.Modulus), 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
p.UnMarshalBinary(data)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func benchmark_GaussPoly(sigma float64, context *Context, b *testing.B) {
|
||||
|
||||
b.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/NewGaussPoly", context.N, len(context.Modulus), 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
context.NewGaussPoly(sigma)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func benchmark_KYSGaussPoly(sigma float64, context *Context, b *testing.B) {
|
||||
|
||||
bound := int(sigma * 6)
|
||||
|
||||
KYS := context.NewKYSampler(sigma, bound)
|
||||
|
||||
pol := context.NewPoly()
|
||||
|
||||
b.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/KYS.Sample", context.N, len(context.Modulus), 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
KYS.Sample(pol)
|
||||
}
|
||||
})
|
||||
|
||||
b.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/KYS.SampleNTT", context.N, len(context.Modulus), 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
KYS.SampleNTT(pol)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func benchmark_TernaryPoly(context *Context, b *testing.B) {
|
||||
|
||||
ternarySampler := context.NewTernarySampler()
|
||||
|
||||
pol := context.NewPoly()
|
||||
|
||||
b.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/context.TernaryPoly", context.N, len(context.Modulus), 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
context.NewTernaryPoly()
|
||||
}
|
||||
})
|
||||
|
||||
b.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/ternarySampler.Sample", context.N, len(context.Modulus), 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ternarySampler.Sample(pol)
|
||||
}
|
||||
})
|
||||
|
||||
b.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/ternarySampler.SampleMontgomery", context.N, len(context.Modulus), 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ternarySampler.SampleMontgomery(pol)
|
||||
}
|
||||
})
|
||||
|
||||
b.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/ternarySampler.SampleMontgomeryNTT", context.N, len(context.Modulus), 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ternarySampler.SampleMontgomeryNTT(pol)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func benchmark_UniformPoly(context *Context, b *testing.B) {
|
||||
|
||||
b.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/NewUniformPoly", context.N, len(context.Modulus), 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
context.NewUniformPoly()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func benchmark_NTT(context *Context, b *testing.B) {
|
||||
|
||||
p := context.NewUniformPoly()
|
||||
b.ResetTimer()
|
||||
|
||||
b.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/NTT", context.N, len(context.Modulus), 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
context.NTT(p, p)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func benchmark_InvNTT(context *Context, b *testing.B) {
|
||||
|
||||
p := context.NewUniformPoly()
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
b.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/InvNTT", context.N, len(context.Modulus), 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
context.InvNTT(p, p)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func benchmark_MulCoeffs(context *Context, b *testing.B) {
|
||||
|
||||
p := context.NewUniformPoly()
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
b.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/MulCoeffs", context.N, len(context.Modulus), 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
context.MulCoeffs(p, p, p)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func benchmark_MulCoeffsMontgomery(context *Context, b *testing.B) {
|
||||
|
||||
p := context.NewUniformPoly()
|
||||
|
||||
context.MForm(p, p)
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
b.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/MulCoeffs_Montgomery", context.N, len(context.Modulus), 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
context.MulCoeffsMontgomery(p, p, p)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func benchmark_MulPoly(context *Context, b *testing.B) {
|
||||
|
||||
p := context.NewUniformPoly()
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
b.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/MulPoly", context.N, len(context.Modulus), 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
context.MulPoly(p, p, p)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func benchmark_MulPolyMontgomery(context *Context, b *testing.B) {
|
||||
|
||||
p := context.NewUniformPoly()
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
b.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/MulPoly_Montgomery", context.N, len(context.Modulus), 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
context.MulPolyMontgomery(p, p, p)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func benchmark_MulPolyNaiveMontgomery(context *Context, b *testing.B) {
|
||||
|
||||
p := context.NewUniformPoly()
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
b.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/MulPoly_Naive_Montgomery", context.N, len(context.Modulus), 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
context.MulPolyNaiveMontgomery(p, p, p)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func benchmark_Add(context *Context, b *testing.B) {
|
||||
|
||||
p := context.NewUniformPoly()
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
b.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/Add", context.N, len(context.Modulus), 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
context.Add(p, p, p)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func benchmark_Sub(context *Context, b *testing.B) {
|
||||
|
||||
p := context.NewUniformPoly()
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
b.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/Sub", context.N, len(context.Modulus), 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
context.Sub(p, p, p)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func benchmark_Neg(context *Context, b *testing.B) {
|
||||
|
||||
p := context.NewUniformPoly()
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
b.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/Neg", context.N, len(context.Modulus), 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
context.Neg(p, p)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func benchmark_MulScalar(context *Context, b *testing.B) {
|
||||
|
||||
p := context.NewUniformPoly()
|
||||
|
||||
scalar := uint64(12345678987654321)
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
b.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/MulScalar", context.N, len(context.Modulus), 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
context.MulScalar(p, scalar, p)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func benchmark_ExtendBasis(contextQ, contextP, contextQP *Context, b *testing.B) {
|
||||
|
||||
BasisExtenderQP, _ := NewBasisExtender(contextQ, contextP)
|
||||
|
||||
p0 := contextQ.NewUniformPoly()
|
||||
p1 := contextQP.NewPoly()
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
b.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/Pi=%dx%dbit/ExtendBasis", contextQ.N, len(contextQ.Modulus), 60, len(contextP.Modulus), 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
BasisExtenderQP.ExtendBasis(p0, p1)
|
||||
}
|
||||
})
|
||||
|
||||
b.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/Pi=%dx%dbit/ExtendBasis_Approximate", contextQ.N, len(contextQ.Modulus), 60, len(contextP.Modulus), 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
BasisExtenderQP.ExtendBasisApproximate(p0, p1)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func benchmark_SimpleScaling(T uint64, context *Context, b *testing.B) {
|
||||
|
||||
SimpleScaler, _ := NewSimpleScaler(T, context)
|
||||
|
||||
p0 := context.NewUniformPoly()
|
||||
p1 := context.NewPoly()
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
b.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/SimpleScaling", context.N, len(context.Modulus), 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
SimpleScaler.Scale(p0, p1)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func benchmark_ComplexScaling(T uint64, contextQ, contextP, contextQP *Context, b *testing.B) {
|
||||
|
||||
ComplexScalerQP, _ := NewComplexScaler(T, contextQ, contextP)
|
||||
|
||||
p0 := contextQP.NewUniformPoly()
|
||||
p1 := contextQ.NewUniformPoly()
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
b.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/Pi=%dx%dbit/ComplexScaling", contextQ.N, len(contextP.Modulus), 60, len(contextQ.Modulus), 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ComplexScalerQP.Scale(p0, p1)
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func benchmark_BRed(b *testing.B) {
|
||||
|
||||
q := uint64(1033576114481528833)
|
||||
u := BRedParams(q)
|
||||
|
||||
x := rand.Uint64() % q
|
||||
y := rand.Uint64() % q
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
b.Run(fmt.Sprintf("Qi=%d/BRed", 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
x = BRed(x, y, q, u)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func benchmark_BRedAdd(b *testing.B) {
|
||||
|
||||
q := uint64(1033576114481528833)
|
||||
u := BRedParams(q)
|
||||
|
||||
x := rand.Uint64()
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
b.Run(fmt.Sprintf("Qi=%dbit/BRedAdd", 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
BRedAdd(x, q, u)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func benchmark_MRed(b *testing.B) {
|
||||
|
||||
q := uint64(1033576114481528833)
|
||||
|
||||
x := rand.Uint64() % q
|
||||
y := rand.Uint64() % q
|
||||
|
||||
u := BRedParams(q)
|
||||
|
||||
y = MForm(y, q, u)
|
||||
|
||||
m := MRedParams(q)
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
b.Run(fmt.Sprintf("Qi=%dbit/MRed", 60), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
x = MRed(x, y, q, m)
|
||||
}
|
||||
})
|
||||
}
|
||||
630
ring/ring_context.go
Normal file
630
ring/ring_context.go
Normal file
@@ -0,0 +1,630 @@
|
||||
package ring
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"encoding/gob"
|
||||
"errors"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
//==============================
|
||||
//===== POLYNOMIAL CONTEXT =====
|
||||
//==============================
|
||||
|
||||
type Context struct {
|
||||
|
||||
// Polynomial nb.Coefficients
|
||||
N uint64
|
||||
|
||||
// Modulies
|
||||
Modulus []uint64
|
||||
|
||||
// 2^bit_length(Qi) - 1
|
||||
mask []uint64
|
||||
|
||||
// Determines if NTT can be used with the current context.
|
||||
validated bool
|
||||
|
||||
// Product of the modulies
|
||||
ModulusBigint *Int
|
||||
|
||||
// Parameters for the CRT reconstruction
|
||||
CrtReconstruction []*Int
|
||||
|
||||
// Fast reduction parameters
|
||||
bredParams [][]uint64
|
||||
mredParams []uint64
|
||||
|
||||
//NTT Parameters
|
||||
psiMont []uint64 //2nth primitive root in montgomery form
|
||||
psiInvMont []uint64 //2nth inverse primitive root in montgomery form
|
||||
|
||||
nttPsi [][]uint64 //powers of the inverse of the 2nth primitive root in montgomery form (in bitreversed order)
|
||||
nttPsiInv [][]uint64 //powers of the inverse of the 2nth primitive root in montgomery form (in bitreversed order)
|
||||
nttNInv []uint64 //[N^-1] mod Qi in montgomery form
|
||||
}
|
||||
|
||||
func NewContext() *Context {
|
||||
return new(Context)
|
||||
}
|
||||
|
||||
func (context *Context) SetParameters(N uint64, Modulus []uint64) error {
|
||||
|
||||
// Checks if N is a power of 2
|
||||
if (N&(N-1)) != 0 && N != 0 {
|
||||
return errors.New("invalid ring degree (must be a power of 2)")
|
||||
}
|
||||
|
||||
context.validated = false
|
||||
|
||||
context.N = N
|
||||
|
||||
context.Modulus = make([]uint64, len(Modulus))
|
||||
context.mask = make([]uint64, len(Modulus))
|
||||
|
||||
for i, qi := range Modulus {
|
||||
context.Modulus[i] = qi
|
||||
context.mask[i] = (1 << uint64(bits.Len64(qi))) - 1
|
||||
}
|
||||
|
||||
//Computes the bigQ
|
||||
context.ModulusBigint = NewInt(1)
|
||||
for _, qi := range context.Modulus {
|
||||
context.ModulusBigint.Mul(context.ModulusBigint, NewUint(qi))
|
||||
}
|
||||
|
||||
// Computes the fast reduction parameters
|
||||
context.bredParams = make([][]uint64, len(context.Modulus))
|
||||
context.mredParams = make([]uint64, len(context.Modulus))
|
||||
|
||||
for i, qi := range context.Modulus {
|
||||
|
||||
//Computes the fast modular reduction parameters for the Context
|
||||
context.bredParams[i] = BRedParams(qi)
|
||||
|
||||
// If qi is not a power of 2, we can compute the MRedParams (else it should not
|
||||
// because it will return an error and there is no valid montgomery form mod a power of 2)
|
||||
if (qi&(qi-1)) != 0 && qi != 0 {
|
||||
context.mredParams[i] = MRedParams(qi)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (context *Context) ValidateParameters() error {
|
||||
|
||||
if context.validated {
|
||||
return nil
|
||||
}
|
||||
|
||||
if context.N == 0 || context.Modulus == nil {
|
||||
return errors.New("error : invalid context parameters (missing)")
|
||||
}
|
||||
|
||||
// CHECKS IF VALIDE NTT
|
||||
// Checks if each qi is Prime and if qi = 1 mod 2n
|
||||
for _, qi := range context.Modulus {
|
||||
if IsPrime(qi) == false || qi&((context.N<<1)-1) != 1 {
|
||||
context.validated = false
|
||||
return errors.New("warning : provided modulus does not allow NTT")
|
||||
}
|
||||
}
|
||||
|
||||
context.CrtReconstruction = make([]*Int, len(context.Modulus))
|
||||
|
||||
context.psiMont = make([]uint64, len(context.Modulus))
|
||||
context.psiInvMont = make([]uint64, len(context.Modulus))
|
||||
context.nttPsi = make([][]uint64, len(context.Modulus))
|
||||
context.nttPsiInv = make([][]uint64, len(context.Modulus))
|
||||
context.nttNInv = make([]uint64, len(context.Modulus))
|
||||
|
||||
bitLenofN := uint64(bits.Len64(context.N) - 1)
|
||||
|
||||
QiB := new(Int)
|
||||
tmp := new(Int)
|
||||
|
||||
for i, qi := range context.Modulus {
|
||||
|
||||
//1.0 CRT reconstruction parameters
|
||||
QiB.SetUint(qi)
|
||||
context.CrtReconstruction[i] = new(Int)
|
||||
context.CrtReconstruction[i].Div(context.ModulusBigint, QiB)
|
||||
tmp.Inv(context.CrtReconstruction[i], QiB)
|
||||
tmp.Mod(tmp, QiB)
|
||||
context.CrtReconstruction[i].Mul(context.CrtReconstruction[i], tmp)
|
||||
|
||||
//2.1 Computes N^(-1) mod Q in Montgomery form
|
||||
context.nttNInv[i] = MForm(modexp(context.N, qi-2, qi), qi, context.bredParams[i])
|
||||
|
||||
//2.2 Computes Psi and PsiInv in Montgomery form
|
||||
context.nttPsi[i] = make([]uint64, context.N)
|
||||
context.nttPsiInv[i] = make([]uint64, context.N)
|
||||
|
||||
//Finds a 2nth primitive Root
|
||||
g := primitiveRoot(qi)
|
||||
|
||||
_2n := uint64(context.N << 1)
|
||||
|
||||
power := (qi - 1) / _2n
|
||||
powerInv := (qi - 1) - power
|
||||
|
||||
//Computes Psi and PsiInv in Montgomery Form
|
||||
PsiMont := MForm(modexp(g, power, qi), qi, context.bredParams[i])
|
||||
PsiInvMont := MForm(modexp(g, powerInv, qi), qi, context.bredParams[i])
|
||||
|
||||
context.psiMont[i] = PsiMont
|
||||
context.psiInvMont[i] = PsiInvMont
|
||||
|
||||
context.nttPsi[i][0] = MForm(1, qi, context.bredParams[i])
|
||||
context.nttPsiInv[i][0] = MForm(1, qi, context.bredParams[i])
|
||||
|
||||
// Computes nttPsi[j] = nttPsi[j-1]*Psi and nttPsiInv[j] = nttPsiInv[j-1]*PsiInv
|
||||
for j := uint64(1); j < context.N; j++ {
|
||||
|
||||
indexReversePrev := bitReverse64(j-1, bitLenofN)
|
||||
indexReverseNext := bitReverse64(j, bitLenofN)
|
||||
|
||||
context.nttPsi[i][indexReverseNext] = MRed(context.nttPsi[i][indexReversePrev], PsiMont, qi, context.mredParams[i])
|
||||
context.nttPsiInv[i][indexReverseNext] = MRed(context.nttPsiInv[i][indexReversePrev], PsiInvMont, qi, context.mredParams[i])
|
||||
}
|
||||
}
|
||||
|
||||
context.validated = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Used to export the context. Minimal information to recover the full context.
|
||||
type smallContext struct {
|
||||
N uint64
|
||||
Modulus []uint64
|
||||
}
|
||||
|
||||
func (context *Context) MarshalBinary() ([]byte, error) {
|
||||
|
||||
parameters := smallContext{context.N, context.Modulus}
|
||||
|
||||
var buf bytes.Buffer
|
||||
enc := gob.NewEncoder(&buf)
|
||||
if err := enc.Encode(parameters); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (context *Context) UnMarshalBinary(data []byte) error {
|
||||
|
||||
parameters := smallContext{}
|
||||
|
||||
reader := bytes.NewReader(data)
|
||||
dec := gob.NewDecoder(reader)
|
||||
if err := dec.Decode(¶meters); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
context.SetParameters(parameters.N, parameters.Modulus)
|
||||
context.ValidateParameters()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Merge merges two context by appending all the element from contextP to the elements of contextQ
|
||||
// Will return an error if contextQ or contextP are not both not validated or both validated
|
||||
func (context *Context) Merge(contextQ, contextP *Context) error {
|
||||
|
||||
if contextQ.N != contextP.N {
|
||||
return errors.New("contexts ring degree to not match")
|
||||
}
|
||||
|
||||
context.N = contextQ.N
|
||||
|
||||
context.Modulus = append(contextQ.Modulus, contextP.Modulus...)
|
||||
context.mask = append(contextQ.mask, contextP.mask...)
|
||||
|
||||
if context != contextQ && context != contextP {
|
||||
context.ModulusBigint = NewUint(0)
|
||||
}
|
||||
|
||||
context.ModulusBigint.Mul(contextQ.ModulusBigint, contextP.ModulusBigint)
|
||||
|
||||
// For this part we need to recompute, since each element is a function of all the other modulus
|
||||
context.CrtReconstruction = append(contextQ.CrtReconstruction, contextP.CrtReconstruction...)
|
||||
QiB := new(Int)
|
||||
tmp := new(Int)
|
||||
for i, qi := range context.Modulus {
|
||||
QiB.SetUint(qi)
|
||||
context.CrtReconstruction[i] = new(Int)
|
||||
context.CrtReconstruction[i].Div(context.ModulusBigint, QiB)
|
||||
tmp.Inv(context.CrtReconstruction[i], QiB)
|
||||
tmp.Mod(tmp, QiB)
|
||||
context.CrtReconstruction[i].Mul(context.CrtReconstruction[i], tmp)
|
||||
}
|
||||
|
||||
context.bredParams = append(contextQ.bredParams, contextP.bredParams...)
|
||||
context.mredParams = append(contextQ.mredParams, contextP.mredParams...)
|
||||
|
||||
context.psiMont = append(contextQ.psiMont, contextP.psiMont...)
|
||||
context.psiInvMont = append(contextQ.psiInvMont, contextP.psiInvMont...)
|
||||
|
||||
if contextQ.validated == false && contextP.validated == false {
|
||||
|
||||
context.validated = false
|
||||
|
||||
} else if contextQ.validated && contextP.validated {
|
||||
|
||||
context.nttPsi = append(contextQ.nttPsi, contextP.nttPsi...)
|
||||
context.nttPsiInv = append(contextQ.nttPsiInv, contextP.nttPsiInv...)
|
||||
context.nttNInv = append(contextQ.nttNInv, contextP.nttNInv...)
|
||||
context.validated = true
|
||||
|
||||
} else {
|
||||
|
||||
return errors.New("context need both to be validated or not validated")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (context *Context) IsValidated() bool {
|
||||
return context.validated
|
||||
}
|
||||
|
||||
func (context *Context) GetBredParams() [][]uint64 {
|
||||
return context.bredParams
|
||||
}
|
||||
|
||||
func (context *Context) GetMredParams() []uint64 {
|
||||
return context.mredParams
|
||||
}
|
||||
|
||||
func (context *Context) GetPsi() []uint64 {
|
||||
return context.psiMont
|
||||
}
|
||||
|
||||
func (context *Context) GetPsiInv() []uint64 {
|
||||
return context.psiInvMont
|
||||
}
|
||||
|
||||
func (context *Context) GetNttPsi() [][]uint64 {
|
||||
return context.nttPsi
|
||||
}
|
||||
|
||||
func (context *Context) GetNttPsiInv() [][]uint64 {
|
||||
return context.nttPsiInv
|
||||
}
|
||||
|
||||
func (context *Context) GetNttNInv() []uint64 {
|
||||
return context.nttNInv
|
||||
}
|
||||
|
||||
// Create a new ring with all coefficients set to 0 from the given context
|
||||
func (context *Context) NewPoly() *Poly {
|
||||
p := new(Poly)
|
||||
|
||||
p.Coeffs = make([][]uint64, len(context.Modulus))
|
||||
for i := 0; i < len(context.Modulus); i++ {
|
||||
p.Coeffs[i] = make([]uint64, context.N)
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
// Generates a ring with coefficients following a
|
||||
// discrete gaussian distribution with derivation sigma
|
||||
func (context *Context) NewGaussPoly(sigma float64) *Poly {
|
||||
|
||||
Pol := context.NewPoly()
|
||||
|
||||
var coeff int64
|
||||
|
||||
boundMultiplier := float64(6) // this parameter is the same as the SEAL library
|
||||
positiveBound := int64(boundMultiplier * sigma) // the suggested sigma from SEAL is 3.19
|
||||
negativeBound := -int64(boundMultiplier * sigma)
|
||||
|
||||
for i := uint64(0); i < context.N; i++ {
|
||||
|
||||
for {
|
||||
|
||||
coeff = GaussSampling(sigma)
|
||||
|
||||
// Samples values until one falls in the desired range
|
||||
if (coeff > positiveBound) || (coeff < negativeBound) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Once the value falls in the desired range, assign it to the coeff, breaks and jumps to next coeff
|
||||
for j, qi := range context.Modulus {
|
||||
Pol.Coeffs[j][i] = CRed(uint64(int64(qi)+coeff), qi)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return Pol
|
||||
}
|
||||
|
||||
// Generates a ring with coefficients following a
|
||||
// uniform distribution over [0, Qi-1]
|
||||
func (context *Context) NewUniformPoly() (Pol *Poly) {
|
||||
|
||||
var randomBytes []byte
|
||||
var randomUint, mask uint64
|
||||
|
||||
Pol = context.NewPoly()
|
||||
|
||||
n := context.N
|
||||
if n < 8 {
|
||||
n = 8
|
||||
}
|
||||
|
||||
randomBytes = make([]byte, n)
|
||||
if _, err := rand.Read(randomBytes); err != nil {
|
||||
panic("crypto rand error")
|
||||
}
|
||||
|
||||
for j, qi := range context.Modulus {
|
||||
|
||||
// Starts by computing the mask
|
||||
mask = (1 << uint64(bits.Len64(qi))) - 1
|
||||
|
||||
// Iterates for each modulus over each coefficient
|
||||
for i := uint64(0); i < context.N; i++ {
|
||||
|
||||
// Samples an integer between [0, qi-1]
|
||||
for {
|
||||
|
||||
// Replenishes the pool if it runs empty
|
||||
if len(randomBytes) < 8 {
|
||||
randomBytes = make([]byte, n)
|
||||
if _, err := rand.Read(randomBytes); err != nil {
|
||||
panic("crypto rand error")
|
||||
}
|
||||
}
|
||||
|
||||
// Reads bytes from the pool
|
||||
randomUint = binary.BigEndian.Uint64(randomBytes[:8]) & mask
|
||||
randomBytes = randomBytes[8:] // Discard the used bytes
|
||||
|
||||
// If the integer is between [0, qi-1], breaks the loop
|
||||
if randomUint < qi {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Pol.Coeffs[j][i] = randomUint
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Generates a ring with coefficients following a
|
||||
// uniform distribution over [-1,0,1] mod each Qi
|
||||
func (context *Context) NewTernaryPoly() *Poly {
|
||||
|
||||
Pol := context.NewPoly()
|
||||
|
||||
var coeff uint64
|
||||
var sign uint64
|
||||
|
||||
for i := uint64(0); i < context.N; i++ {
|
||||
|
||||
coeff, sign = randInt3()
|
||||
|
||||
for j, qi := range context.Modulus {
|
||||
Pol.Coeffs[j][i] = (coeff & (sign * 0xFFFFFFFFFFFFFFFF)) | (((qi * coeff) - coeff) & ((sign ^ 1) * 0xFFFFFFFFFFFFFFFF))
|
||||
}
|
||||
}
|
||||
|
||||
return Pol
|
||||
}
|
||||
|
||||
// Generates a ring with coefficients following a
|
||||
// uniform distribution over [-1,0,1] mod each Qi and applies
|
||||
// the NTT.
|
||||
func (context *Context) NewTernaryPolyNTT() *Poly {
|
||||
|
||||
Pol := context.NewPoly()
|
||||
|
||||
var coeff uint64
|
||||
var sign uint64
|
||||
|
||||
for i := uint64(0); i < context.N; i++ {
|
||||
|
||||
coeff, sign = randInt3()
|
||||
|
||||
for j, qi := range context.Modulus {
|
||||
|
||||
Pol.Coeffs[j][i] = (coeff & (sign * 0xFFFFFFFFFFFFFFFF)) | ((qi * coeff) & ((sign ^ 1) * 0xFFFFFFFFFFFFFFFF))
|
||||
}
|
||||
}
|
||||
|
||||
context.NTT(Pol, Pol)
|
||||
|
||||
return Pol
|
||||
}
|
||||
|
||||
// Generates a ring with coefficients following a
|
||||
// uniform distribution over [-1,0,1] mod each Qi and applies
|
||||
// the NTT and Montgomery form.
|
||||
func (context *Context) NewTernaryPolyMontgomeryNTT() *Poly {
|
||||
|
||||
Pol := context.NewPoly()
|
||||
|
||||
var coeff uint64
|
||||
var sign uint64
|
||||
|
||||
for i := uint64(0); i < context.N; i++ {
|
||||
|
||||
coeff, sign = randInt3()
|
||||
|
||||
for j, qi := range context.Modulus {
|
||||
|
||||
Pol.Coeffs[j][i] = (coeff & (sign * 0xFFFFFFFFFFFFFFFF)) | ((qi - coeff) & ((sign ^ 1) * 0xFFFFFFFFFFFFFFFF))
|
||||
}
|
||||
}
|
||||
|
||||
context.MForm(Pol, Pol)
|
||||
context.NTT(Pol, Pol)
|
||||
|
||||
return Pol
|
||||
}
|
||||
|
||||
// Sets the coefficients of Pol from an int64 array
|
||||
// If a coefficient is negative it will instead assay its
|
||||
// positive inverse mod each Qi
|
||||
func (context *Context) SetCoefficientsInt64(coeffs []int64, p1 *Poly) error {
|
||||
if len(coeffs) != int(context.N) {
|
||||
return errors.New("error : invalid ring degree (does not match context)")
|
||||
}
|
||||
for i, coeff := range coeffs {
|
||||
for j, Qi := range context.Modulus {
|
||||
p1.Coeffs[j][i] = CRed(uint64((coeff%int64(Qi) + int64(Qi))), Qi)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sets the coefficient of Pol from an uint64 array
|
||||
func (context *Context) SetCoefficientsUint64(coeffs []uint64, p1 *Poly) error {
|
||||
if len(coeffs) != int(context.N) {
|
||||
return errors.New("error : invalid ring degree (does not match context)")
|
||||
}
|
||||
for i, coeff := range coeffs {
|
||||
for j, Qi := range context.Modulus {
|
||||
p1.Coeffs[j][i] = coeff % Qi
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sets the coefficients of Pol from a array of strings
|
||||
// It will import them as bigint variables and reduce them mod each Qi.
|
||||
func (context *Context) SetCoefficientsString(coeffs []string, p1 *Poly) error {
|
||||
|
||||
if len(coeffs) != int(context.N) {
|
||||
return errors.New("error : invalid ring degree (does not match context)")
|
||||
}
|
||||
|
||||
QiBigint := new(Int)
|
||||
coeffTmp := new(Int)
|
||||
for i, Qi := range context.Modulus {
|
||||
QiBigint.SetUint(Qi)
|
||||
for j, coeff := range coeffs {
|
||||
p1.Coeffs[i][j] = coeffTmp.Mod(NewIntFromString(coeff), QiBigint).Uint64()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sets the coefficients of Pol from a array of strings
|
||||
// It will import them as bigint variables and
|
||||
// reduce them mod each Qi
|
||||
func (context *Context) SetCoefficientsBigint(coeffs []*Int, p1 *Poly) error {
|
||||
|
||||
if len(coeffs) != int(context.N) {
|
||||
return errors.New("error : invalid ring degree (does not match context)")
|
||||
}
|
||||
|
||||
QiBigint := new(Int)
|
||||
coeffTmp := new(Int)
|
||||
for i, Qi := range context.Modulus {
|
||||
QiBigint.SetUint(Qi)
|
||||
for j, coeff := range coeffs {
|
||||
p1.Coeffs[i][j] = coeffTmp.Mod(coeff, QiBigint).Uint64()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//Returns an string array containing the reconstructed coefficients
|
||||
func (context *Context) PolyToString(p1 *Poly) []string {
|
||||
|
||||
coeffsBigint := context.PolyToBigint(p1)
|
||||
coeffsString := make([]string, len(coeffsBigint))
|
||||
|
||||
for i := range coeffsBigint {
|
||||
coeffsString[i] = coeffsBigint[i].String()
|
||||
}
|
||||
|
||||
return coeffsString
|
||||
}
|
||||
|
||||
//Returns an string array containing the reconstructed coefficients
|
||||
func (context *Context) PolyToBigint(p1 *Poly) []*Int {
|
||||
|
||||
tmp := NewInt(0)
|
||||
|
||||
coeffsBigint := make([]*Int, context.N)
|
||||
|
||||
for x := uint64(0); x < context.N; x++ {
|
||||
|
||||
tmp.SetUint(0)
|
||||
coeffsBigint[x] = NewUint(0)
|
||||
|
||||
for i := 0; i < len(context.Modulus); i++ {
|
||||
coeffsBigint[x].Add(coeffsBigint[x], tmp.Mul(NewUint(p1.Coeffs[i][x]), context.CrtReconstruction[i]))
|
||||
}
|
||||
|
||||
coeffsBigint[x].Mod(coeffsBigint[x], context.ModulusBigint)
|
||||
}
|
||||
|
||||
return coeffsBigint
|
||||
}
|
||||
|
||||
// Returns an array containing the coefficients of Pol
|
||||
// centered arount each [-Qi/2, Qi/2]
|
||||
func (context *Context) GetCenteredCoefficients(p1 *Poly) [][]int64 {
|
||||
|
||||
coeffs := make([][]int64, len(context.Modulus))
|
||||
var qiHalf int64
|
||||
|
||||
for i, qi := range context.Modulus {
|
||||
qiHalf = int64(qi >> 1)
|
||||
coeffs[i] = make([]int64, context.N)
|
||||
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
coeffs[i][j] = int64(p1.Coeffs[i][j])
|
||||
|
||||
if coeffs[i][j] > qiHalf {
|
||||
coeffs[i][j] -= int64(qi)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return coeffs
|
||||
}
|
||||
|
||||
// Checks if two polynomials are equal in the given context
|
||||
// Also applies an exact modular reduction on the two polynomials.
|
||||
func (context *Context) Equal(p1, p2 *Poly) bool {
|
||||
|
||||
//if len(p1.Coeffs) != len(context.Modulus) || len(p2.Coeffs) != len(context.Modulus){
|
||||
// return false
|
||||
//}
|
||||
|
||||
for i := 0; i < len(context.Modulus); i++ {
|
||||
if len(p1.Coeffs[i]) != len(p2.Coeffs[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
context.Reduce(p1, p1)
|
||||
context.Reduce(p2, p2)
|
||||
|
||||
for i := 0; i < len(context.Modulus); i++ {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
if p1.Coeffs[i][j] != p2.Coeffs[i][j] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
184
ring/ring_object.go
Normal file
184
ring/ring_object.go
Normal file
@@ -0,0 +1,184 @@
|
||||
package ring
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// Poly is the structure containing the coefficients of a ring
|
||||
type Poly struct {
|
||||
Coeffs [][]uint64 //Coefficients in CRT representation
|
||||
//Context *Context might be added back at a later time
|
||||
}
|
||||
|
||||
// GetDegree returns the number of coefficients (degree) of the ring
|
||||
func (Pol *Poly) GetDegree() int {
|
||||
return len(Pol.Coeffs[0])
|
||||
}
|
||||
|
||||
// CetLenModulies returns the number of modulies
|
||||
func (Pol *Poly) GetLenModulies() int {
|
||||
return len(Pol.Coeffs)
|
||||
}
|
||||
|
||||
func (Pol *Poly) ExtendModulies() {
|
||||
|
||||
}
|
||||
|
||||
func (Pol *Poly) Zero() {
|
||||
for i := range Pol.Coeffs {
|
||||
for j := range Pol.Coeffs[0] {
|
||||
Pol.Coeffs[i][j] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy creates a new ring with the same coefficients
|
||||
func (Pol *Poly) CopyNew() (p1 *Poly) {
|
||||
p1 = new(Poly)
|
||||
p1.Coeffs = make([][]uint64, len(Pol.Coeffs))
|
||||
for i := range Pol.Coeffs {
|
||||
p1.Coeffs[i] = make([]uint64, len(Pol.Coeffs[i]))
|
||||
for j := range Pol.Coeffs[i] {
|
||||
p1.Coeffs[i][j] = Pol.Coeffs[i][j]
|
||||
}
|
||||
}
|
||||
|
||||
return p1
|
||||
}
|
||||
|
||||
func (context *Context) Copy(p0, p1 *Poly) error {
|
||||
if len(p1.Coeffs) < len(context.Modulus) || uint64(len(p1.Coeffs[0])) < context.N {
|
||||
return errors.New("error : copy Poly, receiver poly is invalide")
|
||||
}
|
||||
for i := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
p1.Coeffs[i][j] = p0.Coeffs[i][j]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copies the coefficient on a receiver ring
|
||||
func (Pol *Poly) Copy(p1 *Poly) error {
|
||||
if len(Pol.Coeffs) > len(p1.Coeffs) || len(Pol.Coeffs[0]) > len(p1.Coeffs[0]) {
|
||||
return errors.New("error : copy Poly, receiver poly is invalide")
|
||||
}
|
||||
for i := range Pol.Coeffs {
|
||||
for j := range Pol.Coeffs[i] {
|
||||
p1.Coeffs[i][j] = Pol.Coeffs[i][j]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetCoefficients sets the coefficients of ring directly from a CRT format (double slice)
|
||||
func (Pol *Poly) SetCoefficients(coeffs [][]uint64) error {
|
||||
|
||||
if len(coeffs) > len(Pol.Coeffs) {
|
||||
return errors.New("error : len(coeffs) > len(Pol.Coeffs")
|
||||
}
|
||||
|
||||
if len(coeffs[0]) > len(Pol.Coeffs[0]) {
|
||||
return errors.New("error : len(coeffs[0]) > len(Pol.Coeffs[0]")
|
||||
}
|
||||
|
||||
for i := range coeffs {
|
||||
for j := range coeffs[0] {
|
||||
Pol.Coeffs[i][j] = coeffs[i][j]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetCoefficients returns a double slice containing the coefficients of the ring
|
||||
func (Pol *Poly) GetCoefficients() [][]uint64 {
|
||||
coeffs := make([][]uint64, len(Pol.Coeffs))
|
||||
|
||||
for i := range Pol.Coeffs {
|
||||
coeffs[i] = make([]uint64, len(Pol.Coeffs[i]))
|
||||
for j := range Pol.Coeffs[i] {
|
||||
coeffs[i][j] = Pol.Coeffs[i][j]
|
||||
}
|
||||
}
|
||||
|
||||
return coeffs
|
||||
}
|
||||
|
||||
// WriteCoeffsTo converts a matrix of coefficients to a byte array
|
||||
func WriteCoeffsTo(pointer, N, numberModuli uint64, coeffs [][]uint64, data []byte) (uint64, error) {
|
||||
tmp := N << 3
|
||||
for i := uint64(0); i < numberModuli; i++ {
|
||||
for j := uint64(0); j < N; j++ {
|
||||
binary.BigEndian.PutUint64(data[pointer+(j<<3):pointer+((j+1)<<3)], coeffs[i][j])
|
||||
}
|
||||
pointer += tmp
|
||||
}
|
||||
|
||||
return pointer, nil
|
||||
}
|
||||
|
||||
// DecodeCoeffs converts a byte array to a matrix of coefficients
|
||||
func DecodeCoeffs(pointer, N, numberModuli uint64, coeffs [][]uint64, data []byte) (uint64, error) {
|
||||
tmp := N << 3
|
||||
for i := uint64(0); i < numberModuli; i++ {
|
||||
coeffs[i] = make([]uint64, N)
|
||||
for j := uint64(0); j < N; j++ {
|
||||
coeffs[i][j] = binary.BigEndian.Uint64(data[pointer+(j<<3) : pointer+((j+1)<<3)])
|
||||
}
|
||||
pointer += tmp
|
||||
}
|
||||
|
||||
return pointer, nil
|
||||
}
|
||||
|
||||
func (Pol *Poly) MarshalBinary() ([]byte, error) {
|
||||
|
||||
N := uint64(len(Pol.Coeffs[0]))
|
||||
numberModulies := uint64(len(Pol.Coeffs))
|
||||
|
||||
data := make([]byte, 2+((N*numberModulies)<<3))
|
||||
|
||||
if numberModulies > 0xFF {
|
||||
return nil, errors.New("error : poly max modulies uint16 overflow")
|
||||
}
|
||||
|
||||
data[0] = uint8(bits.Len64(uint64(N)) - 1)
|
||||
data[1] = uint8(numberModulies)
|
||||
|
||||
var pointer uint64
|
||||
|
||||
pointer = 2
|
||||
|
||||
if _, err := WriteCoeffsTo(pointer, N, numberModulies, Pol.Coeffs, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (Pol *Poly) UnMarshalBinary(data []byte) (*Poly, error) {
|
||||
|
||||
N := uint64(int(1 << data[0]))
|
||||
numberModulies := uint64(int(data[1]))
|
||||
|
||||
Coeffs := make([][]uint64, numberModulies)
|
||||
|
||||
var pointer uint64
|
||||
|
||||
pointer = 2
|
||||
|
||||
if ((uint64(len(data)) - pointer) >> 3) != N*numberModulies {
|
||||
return nil, errors.New("error : invalid ring encoding")
|
||||
}
|
||||
|
||||
if _, err := DecodeCoeffs(pointer, N, numberModulies, Coeffs, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Pol = &Poly{Coeffs}
|
||||
|
||||
return Pol, nil
|
||||
}
|
||||
397
ring/ring_scaling.go
Normal file
397
ring/ring_scaling.go
Normal file
@@ -0,0 +1,397 @@
|
||||
package ring
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
//=========================================
|
||||
//===== CRT SIMPLE SCAliNG PARAMETERS =====
|
||||
//=========================================
|
||||
|
||||
//Algorithm from https://eprint.iacr.org/2018/117.pdf
|
||||
type SimpleScaler struct {
|
||||
context *Context
|
||||
|
||||
t uint64 //Plaintext modulus
|
||||
wi []uint64 //Integer parts of ([Q/Qi]^(-1))_{Qi} * t/Qi
|
||||
ti []Float128 //Fractional part of ([Q/Qi]^(-1))_{Qi} * t/Qi
|
||||
|
||||
reducealgoMul func(x, y uint64) uint64
|
||||
reducealgoAdd func(x uint64) uint64
|
||||
|
||||
reducealgoAddParam uint64
|
||||
reducealgoMulParam uint64
|
||||
}
|
||||
|
||||
func NewSimpleScaler(t uint64, context *Context) (*SimpleScaler, error) {
|
||||
|
||||
if context.validated != true {
|
||||
return nil, errors.New("error : context must be validated before instantiating a new simple scaler")
|
||||
}
|
||||
|
||||
newParams := new(SimpleScaler)
|
||||
|
||||
var tmp Float128
|
||||
var QiB Int // Qi
|
||||
var QiStar Int // Q/Qi
|
||||
var QiBarre Int // (Q/Qi)^(-1) mod Qi
|
||||
|
||||
newParams.t = t
|
||||
newParams.context = context
|
||||
|
||||
// Assigns the correct reduction algorithm depending on the provided t
|
||||
// If t is a power of 2
|
||||
if (t&(t-1)) == 0 && t != 0 {
|
||||
|
||||
newParams.reducealgoAddParam = t - 1
|
||||
newParams.reducealgoMulParam = t - 1
|
||||
|
||||
newParams.reducealgoMul = func(x, y uint64) uint64 {
|
||||
return (x * y) & newParams.reducealgoAddParam
|
||||
}
|
||||
|
||||
newParams.reducealgoAdd = func(x uint64) uint64 {
|
||||
return x & newParams.reducealgoMulParam
|
||||
}
|
||||
|
||||
// Else (we can use montgomery reduction)
|
||||
} else {
|
||||
newParams.reducealgoAddParam = BRedParams(t)[0]
|
||||
newParams.reducealgoMulParam = MRedParams(t)
|
||||
|
||||
newParams.reducealgoMul = func(x, y uint64) uint64 {
|
||||
ahi, alo := bits.Mul64(x, y)
|
||||
R := alo * newParams.reducealgoMulParam
|
||||
H, _ := bits.Mul64(R, newParams.t)
|
||||
r := ahi - H + newParams.t
|
||||
|
||||
if r >= newParams.t {
|
||||
r -= newParams.t
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
newParams.reducealgoAdd = func(x uint64) uint64 {
|
||||
|
||||
s0, _ := bits.Mul64(x, newParams.reducealgoAddParam)
|
||||
|
||||
r := x - s0*newParams.t
|
||||
|
||||
if r >= newParams.t {
|
||||
r -= newParams.t
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
||||
// Integer and rational part of QiBarre * t/Qi
|
||||
newParams.wi = make([]uint64, len(context.Modulus))
|
||||
newParams.ti = make([]Float128, len(context.Modulus))
|
||||
|
||||
for i, qi := range context.Modulus {
|
||||
|
||||
QiB.SetUint(qi)
|
||||
QiStar.Div(context.ModulusBigint, &QiB)
|
||||
QiBarre.Inv(&QiStar, &QiB)
|
||||
QiBarre.Mod(&QiBarre, &QiB)
|
||||
|
||||
tmp = Float128Div(Float128SetUint53(t), Float128SetUint64(qi))
|
||||
|
||||
tmp = Float128Mul(tmp, Float128SetUint64(QiBarre.Uint64()))
|
||||
|
||||
//floor( ([Q/Qi]^(-1))_{Qi} * t/Qi )
|
||||
newParams.wi[i] = Float128ToUint53(tmp)
|
||||
|
||||
// If t is not a power of 2 converts in montgomery form
|
||||
if (t&(t-1)) != 0 && t != 0 {
|
||||
newParams.wi[i] = MForm(newParams.wi[i], t, BRedParams(t))
|
||||
}
|
||||
|
||||
QiBarre.Mul(&QiBarre, NewUint(t))
|
||||
QiBarre.Mod(&QiBarre, &QiB)
|
||||
|
||||
newParams.ti[i] = Float128Div(Float128SetUint64(QiBarre.Uint64()), Float128SetUint64(qi)) //floor( ([Q/Qi]^(-1))_{Qi} * t/Qi ) - ( ([Q/Qi]^(-1))_{Qi} * t/Qi )
|
||||
}
|
||||
|
||||
return newParams, nil
|
||||
}
|
||||
|
||||
// Given a ring in basis Qi
|
||||
// Scales its coefficients by a factor t/Q and returns the result mod t
|
||||
// Since t is smaller than all Qi, all coefficients will be identical
|
||||
// between all Qi
|
||||
func (parameters *SimpleScaler) Scale(p1, p2 *Poly) {
|
||||
|
||||
var a uint64
|
||||
|
||||
var b Float128
|
||||
|
||||
for i := uint64(0); i < parameters.context.N; i++ {
|
||||
|
||||
a = 0
|
||||
b[0], b[1] = 0, 0
|
||||
|
||||
for j := range parameters.context.Modulus {
|
||||
// round(xi*wi + xi*ti)%t
|
||||
a += parameters.reducealgoMul(parameters.wi[j], p1.Coeffs[j][i])
|
||||
|
||||
b = Float128Add(b, Float128Mul(parameters.ti[j], Float128SetUint64(p1.Coeffs[j][i])))
|
||||
}
|
||||
|
||||
a += Float128ToUint64(b)
|
||||
|
||||
p2.Coeffs[0][i] = parameters.reducealgoAdd(a)
|
||||
|
||||
for j := 1; j < len(parameters.context.Modulus); j++ {
|
||||
p2.Coeffs[j][i] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (parameters *SimpleScaler) ScaleFloat(p1 *Poly, coeffs []complex128) {
|
||||
|
||||
var a float64
|
||||
|
||||
var b Float128
|
||||
|
||||
for i := uint64(0); i < parameters.context.N; i++ {
|
||||
|
||||
a = 0
|
||||
b[0], b[1] = 0, 0
|
||||
|
||||
for j, qi := range parameters.context.Modulus {
|
||||
// round(xi*wi + xi*ti)%t
|
||||
coeff := int64(p1.Coeffs[j][i])
|
||||
|
||||
if coeff > int64(qi>>1) {
|
||||
coeff -= int64(qi)
|
||||
}
|
||||
|
||||
fmt.Println(coeff)
|
||||
|
||||
a += float64(int64(parameters.wi[j]) * coeff)
|
||||
|
||||
b = Float128Add(b, Float128Mul(parameters.ti[j], Float128SetInt64(coeff)))
|
||||
}
|
||||
|
||||
a += b[0] * 4096
|
||||
|
||||
coeffs[i] = complex(a, 0)
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================
|
||||
//===== CRT COMPLEX SCAliNG PARAMETERS =====
|
||||
//==========================================
|
||||
|
||||
type ComplexScaler struct {
|
||||
|
||||
// 1. General Parameters
|
||||
contextQ *Context
|
||||
contextP *Context
|
||||
|
||||
// 2. Scaling Parameters
|
||||
|
||||
// Scale factore
|
||||
t uint64
|
||||
// t*P
|
||||
tP *Int
|
||||
|
||||
// Integer part of (t * P)/Qi (mod Qi)
|
||||
wiiMont [][]uint64
|
||||
// Rational part of (t * P)/Qi
|
||||
fi []Float128
|
||||
|
||||
// (t * P * r)/Pj (mod Qi)
|
||||
sijMont [][]uint64
|
||||
|
||||
// (t * P) mod Qi
|
||||
li [][]uint64
|
||||
|
||||
// (Q * P * r)/Qi
|
||||
qpiMont []uint64
|
||||
// (Q * P * r)/Pj
|
||||
qpjMont []uint64
|
||||
|
||||
// Qi and Pi in float128 type
|
||||
qiFloat128 []Float128
|
||||
pjFloat128 []Float128
|
||||
}
|
||||
|
||||
func NewComplexScaler(t uint64, contextQ, contextP *Context) (*ComplexScaler, error) {
|
||||
|
||||
if contextQ.validated != true || contextP.validated != true {
|
||||
return nil, errors.New("error : both contexts must be validated before instantiating a new simple scaler")
|
||||
}
|
||||
|
||||
newParams := new(ComplexScaler)
|
||||
|
||||
newParams.contextQ = contextQ
|
||||
newParams.contextP = contextP
|
||||
|
||||
newParams.t = t
|
||||
newParams.tP = NewInt(0).Mul(NewInt(int64(t)), contextP.ModulusBigint)
|
||||
|
||||
newParams.wiiMont = make([][]uint64, len(contextQ.Modulus))
|
||||
newParams.sijMont = make([][]uint64, len(contextP.Modulus))
|
||||
|
||||
newParams.li = make([][]uint64, len(contextQ.Modulus))
|
||||
newParams.fi = make([]Float128, len(contextQ.Modulus))
|
||||
|
||||
newParams.qpiMont = make([]uint64, len(contextQ.Modulus))
|
||||
newParams.qpjMont = make([]uint64, len(contextP.Modulus))
|
||||
|
||||
newParams.qiFloat128 = make([]Float128, len(contextQ.Modulus))
|
||||
newParams.pjFloat128 = make([]Float128, len(contextP.Modulus))
|
||||
|
||||
var QiB Int
|
||||
var PjB Int
|
||||
var v uint64
|
||||
|
||||
tmp := NewInt(1) // Temporary variable
|
||||
|
||||
QPiStar := make([]Int, len(contextQ.Modulus))
|
||||
QPjStar := make([]Int, len(contextP.Modulus))
|
||||
|
||||
// ModulusBigint Q*P
|
||||
QP := NewInt(1).Mul(contextQ.ModulusBigint, contextP.ModulusBigint)
|
||||
|
||||
wi := make([]Int, len(contextQ.Modulus))
|
||||
|
||||
for i, qi := range contextQ.Modulus {
|
||||
QiB.SetUint(qi)
|
||||
wi[i].Div(newParams.tP, &QiB)
|
||||
}
|
||||
|
||||
for i, qi := range contextQ.Modulus {
|
||||
QiB.SetUint(qi)
|
||||
|
||||
// Integer part of tp/q_i mod each Qi
|
||||
newParams.wiiMont[i] = make([]uint64, len(contextQ.Modulus))
|
||||
for j := 0; j < len(contextQ.Modulus); j++ {
|
||||
newParams.wiiMont[i][j] = MForm(tmp.Mod(&wi[j], &QiB).Uint64(), qi, contextQ.bredParams[i])
|
||||
}
|
||||
|
||||
// Rational part of tp/q_i (mod each Qi)
|
||||
newParams.fi[i] = Float128Div(Float128SetUint64(tmp.Mod(newParams.tP, &QiB).Uint64()), Float128SetUint64(qi))
|
||||
|
||||
// li = -(t*P) mod Qi
|
||||
newParams.li[i] = make([]uint64, len(contextQ.Modulus)+len(contextP.Modulus)+1)
|
||||
v = qi - tmp.Mod(newParams.tP, &QiB).Uint64()
|
||||
newParams.li[i][0] = 0
|
||||
for j := 1; j < (len(contextQ.Modulus) + len(contextP.Modulus) + 1); j++ {
|
||||
newParams.li[i][j] = CRed(newParams.li[i][j-1]+v, qi)
|
||||
}
|
||||
|
||||
// (Q * P * r)/Qi (in Montgomery form)
|
||||
QPiStar[i].Div(QP, &QiB) // QP/Qi
|
||||
|
||||
newParams.qpiMont[i] = MForm(tmp.Inv(&QPiStar[i], &QiB).Uint64(), qi, contextQ.bredParams[i])
|
||||
|
||||
newParams.qiFloat128[i] = Float128SetUint64(qi)
|
||||
}
|
||||
|
||||
for j, pj := range contextP.Modulus {
|
||||
|
||||
// (t*P * r)/Pj mod Qi (in Montgomery form)
|
||||
newParams.sijMont[j] = make([]uint64, len(contextQ.Modulus))
|
||||
PjB.SetUint(pj)
|
||||
for i, qi := range contextQ.Modulus {
|
||||
QiB.SetUint(qi)
|
||||
tmp.Div(newParams.tP, &PjB)
|
||||
tmp.Mod(tmp, &QiB)
|
||||
newParams.sijMont[j][i] = MForm(tmp.Uint64(), qi, contextQ.bredParams[i])
|
||||
}
|
||||
|
||||
// (Q * P * r)/Pj
|
||||
QPjStar[j].Div(QP, &PjB)
|
||||
newParams.qpjMont[j] = MForm(tmp.Inv(&QPjStar[j], &PjB).Uint64(), pj, contextP.bredParams[j])
|
||||
|
||||
newParams.pjFloat128[j] = Float128SetUint64(pj)
|
||||
}
|
||||
|
||||
return newParams, nil
|
||||
}
|
||||
|
||||
// Given a ring in basis {Q0,Q1....Qi,P0,P1...Pj}
|
||||
// Scales the ring by a factor t/Q and returns the result in basis {Q0,Q1....Qi}
|
||||
func (parameters *ComplexScaler) Scale(p1, p2 *Poly) {
|
||||
|
||||
var tmp, yjFLoat128 Float128
|
||||
var v uint64
|
||||
var aInt uint64
|
||||
var aFloat uint64
|
||||
|
||||
yi := make([]uint64, len(parameters.contextQ.Modulus))
|
||||
yj := make([]uint64, len(parameters.contextP.Modulus))
|
||||
yiFloat128 := make([]Float128, len(parameters.contextQ.Modulus))
|
||||
|
||||
// Given a ring represented in basis Q0, P1, P2
|
||||
//
|
||||
// [[a, b, c, d]_Q0
|
||||
// [a, b, c, d]_P1
|
||||
// [a, b, c, d]_P2]
|
||||
//
|
||||
// Loops over each column of coefficient (first a, then b...), and scales it by t/Q and return the result
|
||||
// in base Q0. Then discards the base P.
|
||||
for x := uint64(0); x < parameters.contextQ.N; x++ {
|
||||
|
||||
tmp[0], tmp[1] = 0, 0
|
||||
|
||||
for i, qi := range parameters.contextQ.Modulus {
|
||||
|
||||
yi[i] = MRed(p1.Coeffs[i][x], parameters.qpiMont[i], qi, parameters.contextQ.mredParams[i])
|
||||
|
||||
yiFloat128[i][0] = float64(yi[i] >> 12)
|
||||
yiFloat128[i][1] = float64(yi[i]&0xfff) / float64(4096)
|
||||
|
||||
tmp = Float128Add(tmp, Float128Div(yiFloat128[i], parameters.qiFloat128[i]))
|
||||
}
|
||||
|
||||
for j, pj := range parameters.contextP.Modulus {
|
||||
|
||||
yj[j] = MRed(p1.Coeffs[j+len(parameters.contextQ.Modulus)][x], parameters.qpjMont[j], pj, parameters.contextP.mredParams[j])
|
||||
|
||||
yjFLoat128[0] = float64(yj[j] >> 12)
|
||||
yjFLoat128[1] = float64(yj[j]&0xfff) / float64(4096)
|
||||
|
||||
tmp = Float128Add(tmp, Float128Div(yjFLoat128, parameters.pjFloat128[j]))
|
||||
}
|
||||
|
||||
v = uint64(math.Round(tmp[0]))
|
||||
|
||||
tmp[0], tmp[1] = 0, 0
|
||||
|
||||
for i := range parameters.contextQ.Modulus {
|
||||
tmp = Float128Add(tmp, Float128Mul(yiFloat128[i], parameters.fi[i]))
|
||||
}
|
||||
|
||||
// Isolates the integer part of the float128 from the floatting point part, then adds the rounded floating point
|
||||
// part to the integer part (this prevents occasional rounding errors when the second floating element is negative)
|
||||
aFloat = uint64(tmp[0]*4096) + uint64(math.Round((tmp[0]*4096)-float64(uint64(tmp[0]*4096))+tmp[1]*4096))
|
||||
|
||||
for u, qi := range parameters.contextQ.Modulus {
|
||||
|
||||
aInt = aFloat
|
||||
|
||||
for i := range parameters.contextQ.Modulus {
|
||||
|
||||
aInt = CRed(aInt+MRed(yi[i], parameters.wiiMont[u][i], qi, parameters.contextQ.mredParams[u]), qi)
|
||||
}
|
||||
|
||||
for j := range parameters.contextP.Modulus {
|
||||
|
||||
aInt = CRed(aInt+MRed(yj[j], parameters.sijMont[j][u], qi, parameters.contextQ.mredParams[u]), qi)
|
||||
}
|
||||
|
||||
p2.Coeffs[u][x] = CRed(aInt+parameters.li[u][v], qi)
|
||||
}
|
||||
}
|
||||
|
||||
p2.Coeffs = p2.Coeffs[:len(parameters.contextQ.Modulus)]
|
||||
}
|
||||
614
ring/ring_test.go
Normal file
614
ring/ring_test.go
Normal file
@@ -0,0 +1,614 @@
|
||||
package ring
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"math/bits"
|
||||
"math/rand"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var folder = "test_data/"
|
||||
|
||||
// Name of the test vectors files
|
||||
|
||||
var files_60 = []string{
|
||||
"test_pol_60____8_2",
|
||||
"test_pol_60___16_2",
|
||||
"test_pol_60___32_2",
|
||||
"test_pol_60___64_2",
|
||||
"test_pol_60__128_2",
|
||||
"test_pol_60__256_2",
|
||||
"test_pol_60__512_2",
|
||||
}
|
||||
|
||||
// Name of the test vectors files
|
||||
|
||||
var filesNTT_60 = []string{
|
||||
"test_pol_NTT_60____8_2",
|
||||
"test_pol_NTT_60___16_2",
|
||||
"test_pol_NTT_60___32_2",
|
||||
"test_pol_NTT_60___64_2",
|
||||
"test_pol_NTT_60__128_2",
|
||||
"test_pol_NTT_60__256_2",
|
||||
"test_pol_NTT_60__512_2",
|
||||
}
|
||||
|
||||
func Test_POLYNOMIAL(t *testing.T) {
|
||||
|
||||
for i := uint64(0); i < 1; i++ {
|
||||
|
||||
N := uint64(2 << (12 + i))
|
||||
T := uint64(65537)
|
||||
|
||||
Qi := Qi60[uint64(len(Qi60))-2<<i:]
|
||||
Pi := Pi60[uint64(len(Pi60))-((2<<i)+1):]
|
||||
|
||||
sigma := 3.19
|
||||
|
||||
contextT := NewContext()
|
||||
contextT.SetParameters(N, []uint64{T})
|
||||
contextT.ValidateParameters()
|
||||
|
||||
contextQ := NewContext()
|
||||
contextQ.SetParameters(N, Qi)
|
||||
contextQ.ValidateParameters()
|
||||
|
||||
contextP := NewContext()
|
||||
contextP.SetParameters(N, Pi)
|
||||
contextP.ValidateParameters()
|
||||
|
||||
contextQP := NewContext()
|
||||
contextQP.Merge(contextQ, contextP)
|
||||
|
||||
// ok!
|
||||
test_GenerateNTTPrimes(N, Qi[0], t)
|
||||
|
||||
// ok!
|
||||
test_ImportExportPolyString(contextQ, t)
|
||||
|
||||
// ok!
|
||||
test_Marshaler(contextQ, t)
|
||||
|
||||
// TODO : check that the coefficients are within the bound
|
||||
test_GaussianPoly(sigma, contextQ, t)
|
||||
|
||||
// ok!
|
||||
test_BRed(contextQ, t)
|
||||
|
||||
// ok!
|
||||
test_MRed(contextQ, t)
|
||||
|
||||
// ok!
|
||||
test_Shift(contextQ, t)
|
||||
|
||||
// ok!
|
||||
test_GaloisShift(contextQ, t)
|
||||
|
||||
// ok!
|
||||
test_MForm(contextQ, t)
|
||||
|
||||
// ok!
|
||||
test_MulPoly(contextQ, t)
|
||||
|
||||
// ok!
|
||||
test_MulPoly_Montgomery(contextQ, t)
|
||||
|
||||
// ok!
|
||||
test_ExtendBasis(contextQ, contextP, contextQP, t)
|
||||
|
||||
// ok!
|
||||
test_SimpleScaling(T, contextQ, contextP, t)
|
||||
|
||||
// ok!
|
||||
test_ComplexScaling(T, contextQ, contextP, contextQP, t)
|
||||
|
||||
test_MultByMonomial(contextQ, t)
|
||||
}
|
||||
}
|
||||
|
||||
// Parses a file and return a slice whose elements are each line of the file
|
||||
func getParamsFromString(filename string) []string {
|
||||
|
||||
file, _ := os.Open(filename)
|
||||
|
||||
defer file.Close()
|
||||
|
||||
var lines []string
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
lines = append(lines, scanner.Text())
|
||||
}
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
// creates a context from a slice of string of N and Modulies
|
||||
func constructContextFromString(vs []string) *Context {
|
||||
|
||||
// Get N
|
||||
tmpN, _ := strconv.ParseUint(vs[0], 0, 32)
|
||||
N := uint64(tmpN)
|
||||
|
||||
// Get Qi
|
||||
ModulusString := strings.Split(strings.TrimSpace(vs[1]), " ")
|
||||
|
||||
Modulus := make([]uint64, len(ModulusString))
|
||||
for i := range ModulusString {
|
||||
tmp, _ := strconv.ParseInt(ModulusString[i], 0, 64)
|
||||
Modulus[i] = uint64(tmp)
|
||||
}
|
||||
|
||||
// Generate the context from N and Qi
|
||||
context := NewContext()
|
||||
context.SetParameters(N, Modulus)
|
||||
context.ValidateParameters()
|
||||
|
||||
return context
|
||||
}
|
||||
|
||||
// creates a ring from a slice of string of coefficients
|
||||
func constructPolynomialsFromString(vs []string, context *Context) *Poly {
|
||||
|
||||
// Extracts the coefficients in CRT and in NTT
|
||||
coeffs := make([][]uint64, len(context.Modulus))
|
||||
for i := range context.Modulus {
|
||||
coeffsString := strings.Split(strings.TrimSpace(vs[i+2]), " ")
|
||||
coeffs[i] = make([]uint64, context.N)
|
||||
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
tmp, _ := strconv.ParseInt(coeffsString[j], 0, 64)
|
||||
coeffs[i][j] = uint64(tmp)
|
||||
}
|
||||
}
|
||||
|
||||
//Create the polynomials and assigns the CRT and NTT coefficients
|
||||
pol := context.NewPoly()
|
||||
pol.SetCoefficients(coeffs)
|
||||
|
||||
return pol
|
||||
}
|
||||
|
||||
func test_Vectors_NTT(t *testing.T) {
|
||||
|
||||
for x := uint64(0); x < 7; x++ {
|
||||
|
||||
vs := getParamsFromString(fmt.Sprintf(folder + files_60[x]))
|
||||
vs_ntt := getParamsFromString(fmt.Sprintf(folder + filesNTT_60[x]))
|
||||
|
||||
context := constructContextFromString(vs)
|
||||
|
||||
t.Run(fmt.Sprintf("Test_Vectors_NTT/N=%4d/Qi=%dx%d", context.N, len(context.Modulus), 60), func(t *testing.T) {
|
||||
|
||||
Polx := constructPolynomialsFromString(vs, context)
|
||||
|
||||
PolNTT := constructPolynomialsFromString(vs_ntt, context)
|
||||
|
||||
CRTCoeffs := Polx.GetCoefficients()
|
||||
|
||||
context.NTT(Polx, Polx)
|
||||
|
||||
for i := range context.Modulus {
|
||||
for j := range Polx.Coeffs {
|
||||
if Polx.Coeffs[i][j] != PolNTT.Coeffs[i][j] {
|
||||
t.Errorf("error : NTT coeffs file n°%v: want %v, have %v", x, PolNTT.Coeffs[i][j], Polx.Coeffs[i][j])
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.InvNTT(Polx, Polx)
|
||||
|
||||
for i := range context.Modulus {
|
||||
for j := range Polx.Coeffs {
|
||||
if Polx.Coeffs[i][j] != CRTCoeffs[i][j] {
|
||||
t.Errorf("error : InvNTT coeffs file n°%v: want %v, have %v", x, CRTCoeffs[i][j], Polx.Coeffs[i][j])
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func test_GenerateNTTPrimes(N, Qi uint64, t *testing.T) {
|
||||
|
||||
t.Run(fmt.Sprintf("N=%d/%dbit/GenerateNTTPrimes", N, 60), func(t *testing.T) {
|
||||
|
||||
primes, err := GenerateNTTPrimes(N, Qi, 100, 60, true)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("error : generateNTTPrimes")
|
||||
}
|
||||
|
||||
for _, q := range primes {
|
||||
if bits.Len64(q) != 60 || q&((N<<1)-1) != 1 || IsPrime(q) != true {
|
||||
t.Errorf("error : GenerateNTTPrimes for q = %v", q)
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func test_ImportExportPolyString(context *Context, t *testing.T) {
|
||||
|
||||
t.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/ImportExportPolyString", context.N, len(context.Modulus), 60), func(t *testing.T) {
|
||||
|
||||
p0 := context.NewUniformPoly()
|
||||
p1 := context.NewPoly()
|
||||
|
||||
context.SetCoefficientsString(context.PolyToString(p0), p1)
|
||||
|
||||
if context.Equal(p0, p1) != true {
|
||||
t.Errorf("error : import/export ring from/to string")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func test_Marshaler(context *Context, t *testing.T) {
|
||||
|
||||
t.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/MarshalContext", context.N, len(context.Modulus), 60), func(t *testing.T) {
|
||||
|
||||
data, _ := context.MarshalBinary()
|
||||
|
||||
contextTest := NewContext()
|
||||
contextTest.UnMarshalBinary(data)
|
||||
|
||||
if contextTest.N != context.N {
|
||||
t.Errorf("ERROR encoding/decoding N")
|
||||
}
|
||||
|
||||
for i := range context.Modulus {
|
||||
if contextTest.Modulus[i] != context.Modulus[i] {
|
||||
t.Errorf("ERROR encoding/decoding Modulus")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/MarshalPoly", context.N, len(context.Modulus), 60), func(t *testing.T) {
|
||||
|
||||
p := context.NewUniformPoly()
|
||||
pTest := context.NewPoly()
|
||||
|
||||
data, _ := p.MarshalBinary()
|
||||
|
||||
pTest, _ = pTest.UnMarshalBinary(data)
|
||||
|
||||
for i := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
if p.Coeffs[i][j] != pTest.Coeffs[i][j] {
|
||||
t.Errorf("PolyBytes Import Error : want %v, have %v", p.Coeffs[i][j], pTest.Coeffs[i][j])
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func test_GaussianPoly(sigma float64, context *Context, t *testing.T) {
|
||||
|
||||
context.NewGaussPoly(sigma)
|
||||
|
||||
bound := int(sigma * 6)
|
||||
KYS := context.NewKYSampler(sigma, bound)
|
||||
pol := context.NewPoly()
|
||||
|
||||
t.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/NewGaussPoly", context.N, len(context.Modulus), 60), func(t *testing.T) {
|
||||
KYS.Sample(pol)
|
||||
context.NewUniformPoly()
|
||||
context.NewTernaryPoly()
|
||||
})
|
||||
}
|
||||
|
||||
func test_BRed(context *Context, t *testing.T) {
|
||||
|
||||
t.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/BRed", context.N, len(context.Modulus), 60), func(t *testing.T) {
|
||||
for j, q := range context.Modulus {
|
||||
|
||||
bigQ := NewUint(q)
|
||||
|
||||
for i := 0; i < 65536; i++ {
|
||||
x := rand.Uint64() % q
|
||||
y := rand.Uint64() % q
|
||||
|
||||
result := NewUint(x)
|
||||
result.Mul(result, NewUint(y))
|
||||
result.Mod(result, bigQ)
|
||||
|
||||
test := BRed(x, y, q, context.bredParams[j])
|
||||
want := result.Uint64()
|
||||
|
||||
if test != want {
|
||||
t.Errorf("error : 128bit barrett multiplication, x = %v, y=%v, have = %v, want =%v", x, y, test, want)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func test_MRed(context *Context, t *testing.T) {
|
||||
|
||||
t.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/MRed", context.N, len(context.Modulus), 60), func(t *testing.T) {
|
||||
for j := range context.Modulus {
|
||||
|
||||
q := context.Modulus[j]
|
||||
|
||||
bigQ := NewUint(q)
|
||||
|
||||
for i := 0; i < 65536; i++ {
|
||||
|
||||
x := rand.Uint64() % q
|
||||
y := rand.Uint64() % q
|
||||
|
||||
result := NewUint(x)
|
||||
result.Mul(result, NewUint(y))
|
||||
result.Mod(result, bigQ)
|
||||
|
||||
test := MRed(x, MForm(y, q, context.bredParams[j]), q, context.mredParams[j])
|
||||
want := result.Uint64()
|
||||
|
||||
if test != want {
|
||||
t.Errorf("error : 128bit montgomery multiplication, x = %v, y=%v, have = %v, want =%v", x, y, test, want)
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func test_Shift(context *Context, t *testing.T) {
|
||||
|
||||
t.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/Shift", context.N, len(context.Modulus), 60), func(t *testing.T) {
|
||||
pWant := context.NewUniformPoly()
|
||||
pTest := context.NewPoly()
|
||||
|
||||
context.Shift(pTest, 1, pWant)
|
||||
for i := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
if pTest.Coeffs[i][(j+1)%context.N] != pWant.Coeffs[i][j] {
|
||||
t.Errorf("error : ShiftR")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func test_GaloisShift(context *Context, t *testing.T) {
|
||||
|
||||
t.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/GaloisShift", context.N, len(context.Modulus), 60), func(t *testing.T) {
|
||||
|
||||
pWant := context.NewUniformPoly()
|
||||
pTest := pWant.CopyNew()
|
||||
|
||||
context.BitReverse(pTest, pTest)
|
||||
context.InvNTT(pTest, pTest)
|
||||
context.Rotate(pTest, 1, pTest)
|
||||
context.NTT(pTest, pTest)
|
||||
context.BitReverse(pTest, pTest)
|
||||
context.Reduce(pTest, pTest)
|
||||
|
||||
context.Shift(pWant, 1, pWant)
|
||||
|
||||
for i := range context.Modulus {
|
||||
for j := uint64(0); j < context.N; j++ {
|
||||
if pTest.Coeffs[i][j] != pWant.Coeffs[i][j] {
|
||||
t.Errorf("error : GaloisShiftR")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func test_MForm(context *Context, t *testing.T) {
|
||||
|
||||
t.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/MForm", context.N, len(context.Modulus), 60), func(t *testing.T) {
|
||||
|
||||
polWant := context.NewUniformPoly()
|
||||
polTest := context.NewPoly()
|
||||
|
||||
context.MForm(polWant, polTest)
|
||||
context.InvMForm(polTest, polTest)
|
||||
|
||||
if context.Equal(polWant, polTest) != true {
|
||||
t.Errorf("error : 128bit MForm/InvMForm")
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func test_MulPoly(context *Context, t *testing.T) {
|
||||
|
||||
t.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/MulPoly", context.N, len(context.Modulus), 60), func(t *testing.T) {
|
||||
|
||||
p1 := context.NewUniformPoly()
|
||||
p2 := context.NewUniformPoly()
|
||||
p3Test := context.NewPoly()
|
||||
p3Want := context.NewPoly()
|
||||
|
||||
context.Reduce(p1, p1)
|
||||
context.Reduce(p2, p2)
|
||||
|
||||
context.MulPolyNaive(p1, p2, p3Want)
|
||||
|
||||
context.MulPoly(p1, p2, p3Test)
|
||||
|
||||
for i := uint64(0); i < context.N; i++ {
|
||||
if p3Want.Coeffs[0][i] != p3Test.Coeffs[0][i] {
|
||||
t.Errorf("ERROR MUL COEFF %v, want %v - has %v", i, p3Want.Coeffs[0][i], p3Test.Coeffs[0][i])
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func test_MulPoly_Montgomery(context *Context, t *testing.T) {
|
||||
|
||||
t.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/MulPoly_Montgomery", context.N, len(context.Modulus), 60), func(t *testing.T) {
|
||||
p1 := context.NewUniformPoly()
|
||||
p2 := context.NewUniformPoly()
|
||||
p3Test := context.NewPoly()
|
||||
p3Want := context.NewPoly()
|
||||
|
||||
context.MForm(p1, p1)
|
||||
context.MForm(p2, p2)
|
||||
|
||||
context.MulPolyNaiveMontgomery(p1, p2, p3Want)
|
||||
context.MulPolyMontgomery(p1, p2, p3Test)
|
||||
|
||||
context.InvMForm(p3Test, p3Test)
|
||||
context.InvMForm(p3Want, p3Want)
|
||||
|
||||
for i := uint64(0); i < context.N; i++ {
|
||||
if p3Want.Coeffs[0][i] != p3Test.Coeffs[0][i] {
|
||||
t.Errorf("ERROR MUL COEFF %v, want %v - has %v", i, p3Want.Coeffs[0][i], p3Test.Coeffs[0][i])
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func test_ExtendBasis(contextQ, contextP, contextQP *Context, t *testing.T) {
|
||||
|
||||
t.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/Pi=%dx%d/ExtendBasis", contextQ.N, len(contextQ.Modulus), 60, len(contextP.Modulus), 60), func(t *testing.T) {
|
||||
|
||||
basisextender, _ := NewBasisExtender(contextQ, contextP)
|
||||
|
||||
coeffs := make([]*Int, contextQ.N)
|
||||
for i := uint64(0); i < contextQ.N; i++ {
|
||||
coeffs[i] = RandInt(contextQ.ModulusBigint)
|
||||
}
|
||||
|
||||
PolTest := contextQ.NewPoly()
|
||||
PolWant := contextQP.NewPoly()
|
||||
|
||||
contextQ.SetCoefficientsBigint(coeffs, PolTest)
|
||||
contextQP.SetCoefficientsBigint(coeffs, PolWant)
|
||||
|
||||
basisextender.ExtendBasis(PolTest, PolTest)
|
||||
|
||||
for i := range contextQP.Modulus {
|
||||
for j := uint64(0); j < contextQ.N; j++ {
|
||||
if PolTest.Coeffs[i][j] != PolWant.Coeffs[i][j] {
|
||||
t.Errorf("error extendBasis, want %v - has %v", PolTest.Coeffs[i][j], PolWant.Coeffs[i][j])
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func test_SimpleScaling(T uint64, contextT, contextQ *Context, t *testing.T) {
|
||||
|
||||
t.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/T=%v/SimpleScaling", contextQ.N, len(contextQ.Modulus), 60, T), func(t *testing.T) {
|
||||
|
||||
rescaler, _ := NewSimpleScaler(T, contextQ)
|
||||
|
||||
coeffs := make([]*Int, contextQ.N)
|
||||
for i := uint64(0); i < contextQ.N; i++ {
|
||||
coeffs[i] = RandInt(contextQ.ModulusBigint)
|
||||
}
|
||||
|
||||
coeffsWant := make([]*Int, contextQ.N)
|
||||
for i := range coeffs {
|
||||
coeffsWant[i] = Copy(coeffs[i])
|
||||
coeffsWant[i].Mul(coeffsWant[i], NewUint(T))
|
||||
coeffsWant[i].DivRound(coeffsWant[i], contextQ.ModulusBigint)
|
||||
coeffsWant[i].Mod(coeffsWant[i], NewUint(T))
|
||||
}
|
||||
|
||||
PolTest := contextQ.NewPoly()
|
||||
PolWant := contextT.NewPoly()
|
||||
|
||||
contextQ.SetCoefficientsBigint(coeffs, PolTest)
|
||||
|
||||
rescaler.Scale(PolTest, PolTest)
|
||||
|
||||
contextT.SetCoefficientsBigint(coeffsWant, PolWant)
|
||||
|
||||
for i := uint64(0); i < contextT.N; i++ {
|
||||
if PolWant.Coeffs[0][i] != PolTest.Coeffs[0][i] {
|
||||
t.Errorf("error : simple scaling, want %v have %v", PolWant.Coeffs[0][i], PolTest.Coeffs[0][i])
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func test_ComplexScaling(T uint64, contextQ, contextP, contextQP *Context, t *testing.T) {
|
||||
|
||||
t.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/Pi=%dx%d/T=%d/ComplexScaling", contextQ.N, len(contextQ.Modulus), 60, len(contextP.Modulus), 60, T), func(t *testing.T) {
|
||||
|
||||
complexRescaler, _ := NewComplexScaler(T, contextQ, contextP)
|
||||
|
||||
coeffs := make([]*Int, contextQ.N)
|
||||
for i := uint64(0); i < contextQ.N; i++ {
|
||||
coeffs[i] = RandInt(contextQP.ModulusBigint)
|
||||
coeffs[i].Div(coeffs[i], NewUint(10))
|
||||
}
|
||||
|
||||
//coeffs[0].SetString("323702478295050366752968655518337494486119222600846780736994466085672189264880022173262")
|
||||
//coeffs[1].SetString("4888963624565002661780158142973932724425930851432042682757127743238542390594193351997723")
|
||||
//coeffs[2].SetString("4282984724308796735476641110474688971631286863366811526364125630744789177542625267835653")
|
||||
//coeffs[3].SetString("3443920543591757808834812628691980702836925006139799151579776655221448318527620147901056")
|
||||
//coeffs[4].SetString("2435992701314338051709289453765710565896473610780678347020688344988696126710239045561833")
|
||||
//coeffs[5].SetString("3577369130953681317513231564259583867075569841269600848965005473743746265741712227949357")
|
||||
//coeffs[6].SetString("3650829186364460588505284366902239740496861722443649340705308513232282111686882618728671")
|
||||
//coeffs[7].SetString("5413725621145553596020326503684835624178908809180347713780542414099674315138466376924388")
|
||||
//coeffs[8].SetString("2921703598815986183949445434906117845024974748774925667243895094611669622018594882674160")
|
||||
|
||||
coeffsWant := make([]*Int, contextQ.N)
|
||||
for i := range coeffs {
|
||||
coeffsWant[i] = Copy(coeffs[i])
|
||||
coeffsWant[i].Mul(coeffsWant[i], NewUint(T))
|
||||
coeffsWant[i].DivRound(coeffsWant[i], contextQ.ModulusBigint)
|
||||
}
|
||||
|
||||
PolTest := contextQP.NewPoly()
|
||||
PolWant := contextQ.NewPoly()
|
||||
|
||||
contextQP.SetCoefficientsBigint(coeffs, PolTest)
|
||||
|
||||
complexRescaler.Scale(PolTest, PolTest)
|
||||
contextQ.SetCoefficientsBigint(coeffsWant, PolWant)
|
||||
|
||||
for i := uint64(0); i < contextQ.N; i++ {
|
||||
for j := 0; j < len(contextQ.Modulus); j++ {
|
||||
if PolWant.Coeffs[j][i] != PolTest.Coeffs[j][i] {
|
||||
t.Errorf("error : complex scaling coeff %v Qi%v = %s, want %v have %v", i, j, coeffs[i].String(), PolWant.Coeffs[j][i], PolTest.Coeffs[j][i])
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func test_MultByMonomial(context *Context, t *testing.T) {
|
||||
|
||||
t.Run(fmt.Sprintf("N=%d/Qi=%dx%dbit/MultByMonomial", context.N, len(context.Modulus), 60), func(t *testing.T) {
|
||||
|
||||
p1 := context.NewUniformPoly()
|
||||
|
||||
p3Test := context.NewPoly()
|
||||
p3Want := context.NewPoly()
|
||||
|
||||
context.MultByMonomial(p1, 1, p3Test)
|
||||
context.MultByMonomial(p3Test, 8, p3Test)
|
||||
|
||||
context.MultByMonomial(p1, 9, p3Want)
|
||||
|
||||
for i := uint64(0); i < context.N; i++ {
|
||||
if p3Want.Coeffs[0][i] != p3Test.Coeffs[0][i] {
|
||||
t.Errorf("ERROR MUL BY MONOMIAL %v, want %v - has %v", i, p3Want.Coeffs[0][i], p3Test.Coeffs[0][i])
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
450
ring/sampler.go
Normal file
450
ring/sampler.go
Normal file
@@ -0,0 +1,450 @@
|
||||
package ring
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"math"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// This code is to sample a value from discrete gaussian distribution.
|
||||
// All the algorithms originate from https://eprint.iacr.org/2013/383.pdf
|
||||
|
||||
// GaussSampling returns a value sampled from discrete gaussian distribution,
|
||||
// originates from Algorithm 11 & 12 in the paper.
|
||||
func GaussSampling(sigma float64) int64 {
|
||||
const sigma2 = 0.8493218 // sigma2 = sqrt(1/(2*ln2)) -- page 28
|
||||
k := uint64(math.Round(sigma / sigma2)) // sigma = k * sigma2 -- page 29
|
||||
var x, y, z uint64
|
||||
var b1, b2 bool
|
||||
for {
|
||||
x = binaryGauss()
|
||||
y = randUniform(k)
|
||||
b1 = bernoulliExp(y*(y+2*k*x), 2*sigma*sigma)
|
||||
if b1 {
|
||||
z = k*x + y
|
||||
b2 = bernoulli(0.5)
|
||||
if z != 0 || b2 {
|
||||
if b2 {
|
||||
return int64(z)
|
||||
} else {
|
||||
return -int64(z)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// binaryGauss returns a random uint value drawn from binary gaussian distribution,
|
||||
// originates from Algorithm 10 in the paper.
|
||||
func binaryGauss() uint64 {
|
||||
if bernoulli(0.5) == false {
|
||||
return 0
|
||||
}
|
||||
// i < 16 represents infinite
|
||||
for i := 1; i < 16; i++ {
|
||||
randomBits := randInt64(uint64(2*i - 1))
|
||||
if randomBits != 0 {
|
||||
return binaryGauss()
|
||||
}
|
||||
if randomBits == 0 {
|
||||
return uint64(i)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// bernoulli returns a random bool value drawn from exponential bernoulli distribution
|
||||
// originates from Algorithm 8 in the paper.
|
||||
func bernoulliExp(x uint64, f float64) bool {
|
||||
xBinary, xBitlen := NewInt(int64(x)).Bits()
|
||||
if xBitlen == 0 {
|
||||
return true
|
||||
}
|
||||
for i := xBitlen; i > 0; i-- {
|
||||
if xBinary[i-1] == 1 {
|
||||
c := math.Exp(-math.Exp2(float64(i)) / f)
|
||||
if bernoulli(c) == false {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// bernoulli returns a random bool value drawn from bernoulli distribution
|
||||
func bernoulli(p float64) bool {
|
||||
pInt := uint64(p * (1 << 31))
|
||||
randomInt := randInt32(0xFFFFFFFF)
|
||||
if randomInt < pInt {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type KYSampler struct {
|
||||
context *Context
|
||||
sigma float64
|
||||
bound int
|
||||
Matrix [][]uint8
|
||||
}
|
||||
|
||||
func (context *Context) NewKYSampler(sigma float64, bound int) *KYSampler {
|
||||
kysampler := new(KYSampler)
|
||||
kysampler.context = context
|
||||
kysampler.sigma = sigma
|
||||
kysampler.bound = bound
|
||||
kysampler.Matrix = computeMatrix(sigma, bound)
|
||||
return kysampler
|
||||
}
|
||||
|
||||
//2.50662827463100050241576528481104525300698674060993831662992357 = sqrt(2*pi)
|
||||
func gaussian(x, sigma float64) float64 {
|
||||
return (1 / (sigma * 2.5066282746310007)) * math.Exp(-((math.Pow(x, 2)) / (2 * sigma * sigma)))
|
||||
}
|
||||
|
||||
// Computes the binary expension with precision x in bits of the normal distribution
|
||||
// with sigma and bound. Returns a matrix of the form M = [[0,1,0,0,...],[0,0,1,,0,...]],
|
||||
// where each row is the binary expension of the normal distribution of index(row) with sigma and bound (center=0).
|
||||
func computeMatrix(sigma float64, bound int) [][]uint8 {
|
||||
var g float64
|
||||
var x uint64
|
||||
|
||||
precision := uint64(56)
|
||||
|
||||
M := make([][]uint8, bound)
|
||||
|
||||
breakCounter := 0
|
||||
|
||||
for i := 0; i < bound; i++ {
|
||||
|
||||
g = gaussian(float64(i), sigma)
|
||||
|
||||
if i == 0 {
|
||||
g *= math.Exp2(float64(precision) - 1)
|
||||
} else {
|
||||
g *= math.Exp2(float64(precision))
|
||||
}
|
||||
|
||||
x = uint64(g)
|
||||
|
||||
if x == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
M[i] = make([]uint8, precision-1)
|
||||
|
||||
for j := uint64(0); j < precision-1; j++ {
|
||||
M[i][j] = uint8((x >> (precision - j - 2)) & 1)
|
||||
}
|
||||
|
||||
breakCounter += 1
|
||||
}
|
||||
|
||||
M = M[:breakCounter]
|
||||
|
||||
return M
|
||||
}
|
||||
|
||||
func (kys *KYSampler) SampleNew() *Poly {
|
||||
Pol := kys.context.NewPoly()
|
||||
kys.Sample(Pol)
|
||||
return Pol
|
||||
}
|
||||
|
||||
func (kys *KYSampler) Sample(Pol *Poly) {
|
||||
|
||||
var coeff uint64
|
||||
var sign uint64
|
||||
|
||||
randomBytes := make([]byte, 8)
|
||||
pointer := uint8(0)
|
||||
|
||||
if _, err := rand.Read(randomBytes); err != nil {
|
||||
panic("crypto rand error")
|
||||
}
|
||||
|
||||
for i := uint64(0); i < kys.context.N; i++ {
|
||||
|
||||
coeff, sign, randomBytes, pointer = KYSampling(kys.Matrix, randomBytes, pointer)
|
||||
|
||||
for j, qi := range kys.context.Modulus {
|
||||
|
||||
Pol.Coeffs[j][i] = (coeff & (sign * 0xFFFFFFFFFFFFFFFF)) | ((qi - coeff) & ((sign ^ 1) * 0xFFFFFFFFFFFFFFFF))
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (kys *KYSampler) SampleNTTNew() *Poly {
|
||||
Pol := kys.SampleNew()
|
||||
kys.context.NTT(Pol, Pol)
|
||||
return Pol
|
||||
}
|
||||
|
||||
func (kys *KYSampler) SampleNTT(Pol *Poly) {
|
||||
kys.Sample(Pol)
|
||||
kys.context.NTT(Pol, Pol)
|
||||
}
|
||||
|
||||
func KYSampling(M [][]uint8, randomBytes []byte, pointer uint8) (uint64, uint64, []byte, uint8) {
|
||||
|
||||
var sign uint8
|
||||
|
||||
d := 0
|
||||
col := 0
|
||||
colLen := len(M)
|
||||
|
||||
for {
|
||||
|
||||
// Uses one random byte per cycle and cycle through the randombytes
|
||||
for i := pointer; i < 8; i++ {
|
||||
|
||||
d = (d << 1) + 1 - int((uint8(randomBytes[0])>>i)&1)
|
||||
|
||||
// There is small probability that it will get out of the bound, then
|
||||
// rerun until it gets a proper output
|
||||
if d > colLen-1 {
|
||||
return KYSampling(M, randomBytes, i)
|
||||
}
|
||||
|
||||
for row := colLen - 1; row >= 0; row-- {
|
||||
|
||||
d -= int(M[row][col])
|
||||
|
||||
if d == -1 {
|
||||
|
||||
// Sign
|
||||
if i == 7 {
|
||||
pointer = 0
|
||||
// If the last bit of the byte was read, samples a new byte for the sign
|
||||
randomBytes = randomBytes[1:]
|
||||
|
||||
if len(randomBytes) == 0 {
|
||||
randomBytes = make([]byte, 8)
|
||||
if _, err := rand.Read(randomBytes); err != nil {
|
||||
panic("crypto rand error")
|
||||
}
|
||||
}
|
||||
|
||||
sign = uint8(randomBytes[0]) & 1
|
||||
|
||||
} else {
|
||||
pointer = i
|
||||
// Else the sign is the next bit of the byte
|
||||
sign = uint8(randomBytes[0]>>(i+1)) & 1
|
||||
}
|
||||
|
||||
return uint64(row), uint64(sign), randomBytes, pointer + 1
|
||||
}
|
||||
}
|
||||
|
||||
col += 1
|
||||
}
|
||||
|
||||
// Resets the bit pointer and discards the used byte
|
||||
pointer = 0
|
||||
randomBytes = randomBytes[1:]
|
||||
|
||||
// Sample 8 new bytes if the last byte was discarded
|
||||
if len(randomBytes) == 0 {
|
||||
randomBytes = make([]byte, 8)
|
||||
if _, err := rand.Read(randomBytes); err != nil {
|
||||
panic("crypto rand error")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
type TernarySampler struct {
|
||||
context *Context
|
||||
Matrix [][]uint64
|
||||
MatrixMontgomery [][]uint64
|
||||
}
|
||||
|
||||
func (context *Context) NewTernarySampler() *TernarySampler {
|
||||
|
||||
sampler := new(TernarySampler)
|
||||
sampler.context = context
|
||||
|
||||
sampler.Matrix = make([][]uint64, len(context.Modulus))
|
||||
sampler.MatrixMontgomery = make([][]uint64, len(context.Modulus))
|
||||
|
||||
for i, Qi := range context.Modulus {
|
||||
|
||||
sampler.Matrix[i] = make([]uint64, 3)
|
||||
sampler.Matrix[i][0] = Qi - 1
|
||||
sampler.Matrix[i][1] = 0
|
||||
sampler.Matrix[i][2] = 1
|
||||
|
||||
sampler.MatrixMontgomery[i] = make([]uint64, 3)
|
||||
sampler.MatrixMontgomery[i][0] = MForm(Qi-1, Qi, context.bredParams[i])
|
||||
sampler.MatrixMontgomery[i][1] = 0
|
||||
sampler.MatrixMontgomery[i][2] = MForm(1, Qi, context.bredParams[i])
|
||||
}
|
||||
|
||||
return sampler
|
||||
}
|
||||
|
||||
func (sampler *TernarySampler) SampleNew() (pol *Poly) {
|
||||
pol = sampler.context.NewPoly()
|
||||
sampler.Sample(pol)
|
||||
return pol
|
||||
}
|
||||
|
||||
func (sampler *TernarySampler) Sample(pol *Poly) {
|
||||
var coeff uint64
|
||||
for j := uint64(0); j < sampler.context.N; j++ {
|
||||
coeff = randUint3()
|
||||
for i := range sampler.context.Modulus {
|
||||
pol.Coeffs[i][j] = sampler.Matrix[i][coeff]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (sampler *TernarySampler) SampleMontgomeryNew() (pol *Poly) {
|
||||
pol = sampler.context.NewPoly()
|
||||
sampler.SampleMontgomery(pol)
|
||||
return pol
|
||||
}
|
||||
|
||||
func (sampler *TernarySampler) SampleMontgomery(pol *Poly) {
|
||||
var coeff uint64
|
||||
for j := uint64(0); j < sampler.context.N; j++ {
|
||||
coeff = randUint3()
|
||||
for i := range sampler.context.Modulus {
|
||||
pol.Coeffs[i][j] = sampler.MatrixMontgomery[i][coeff]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (sampler *TernarySampler) SampleNTTNew() (pol *Poly) {
|
||||
pol = sampler.context.NewPoly()
|
||||
sampler.Sample(pol)
|
||||
sampler.context.NTT(pol, pol)
|
||||
return pol
|
||||
}
|
||||
|
||||
func (sampler *TernarySampler) SampleNTT(pol *Poly) {
|
||||
sampler.Sample(pol)
|
||||
sampler.context.NTT(pol, pol)
|
||||
}
|
||||
|
||||
func (sampler *TernarySampler) SampleMontgomeryNTTNew() (pol *Poly) {
|
||||
pol = sampler.SampleMontgomeryNew()
|
||||
sampler.context.NTT(pol, pol)
|
||||
return pol
|
||||
}
|
||||
|
||||
func (sampler *TernarySampler) SampleMontgomeryNTT(pol *Poly) {
|
||||
sampler.SampleMontgomery(pol)
|
||||
sampler.context.NTT(pol, pol)
|
||||
}
|
||||
|
||||
// TODO : precompute the mask in the ring context
|
||||
// randUniform returns a uniformly distributed value in {0,v)
|
||||
func randUniform(v uint64) uint64 {
|
||||
var length, mask, randomInt uint64
|
||||
|
||||
length = uint64(bits.Len64(v))
|
||||
|
||||
mask = (1 << length) - 1
|
||||
|
||||
for {
|
||||
randomInt = randInt64(mask)
|
||||
if randomInt < v {
|
||||
return randomInt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func RandUniform(v uint64) uint64 {
|
||||
var length, mask, randomInt uint64
|
||||
|
||||
length = uint64(bits.Len64(v))
|
||||
|
||||
mask = (1 << length) - 1
|
||||
|
||||
for {
|
||||
randomInt = randInt64(mask)
|
||||
if randomInt < v {
|
||||
return randomInt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Samples an bit and a sign with rejection sampling (25% chance of failure)
|
||||
// with probabilities :
|
||||
// Pr[int = 0 : 1/3 ; int = 1 : 2/3]
|
||||
// Pr[sign = 1 : 1/2; sign = 0 : 1/2]
|
||||
func randInt3() (uint64, uint64) {
|
||||
var randomInt uint64
|
||||
|
||||
for {
|
||||
randomInt = randInt8()
|
||||
if (randomInt & 3) < 3 {
|
||||
// (a|b) is 1/3 = 0 and 2/3 = 1 if (a<<1 | b) in [0,1,2]
|
||||
return ((randomInt >> 1) | randomInt) & 1, (randomInt >> 2) & 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func randUint3() uint64 {
|
||||
var randomInt uint64
|
||||
for {
|
||||
randomInt = randInt8() & 3
|
||||
if randomInt < 3 {
|
||||
return randomInt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO : precompute the mask in the ring context
|
||||
// randInt generates a random uint32 value of given length
|
||||
func randInt8() uint64 {
|
||||
|
||||
// generate random 4 bytes
|
||||
randomBytes := make([]byte, 1)
|
||||
if _, err := rand.Read(randomBytes); err != nil {
|
||||
panic("crypto rand error")
|
||||
}
|
||||
// return required bits
|
||||
return uint64(randomBytes[0])
|
||||
}
|
||||
|
||||
// TODO : precompute the mask in the ring context
|
||||
// randInt generates a random uint32 value of given length
|
||||
func randInt32(mask uint64) uint64 {
|
||||
|
||||
// generate random 4 bytes
|
||||
randomBytes := make([]byte, 4)
|
||||
_, err := rand.Read(randomBytes)
|
||||
if err != nil {
|
||||
panic("crypto rand error")
|
||||
}
|
||||
|
||||
// convert 4 bytes to a uint32
|
||||
randomUint32 := uint64(binary.BigEndian.Uint32(randomBytes))
|
||||
|
||||
// return required bits
|
||||
return mask & randomUint32
|
||||
}
|
||||
|
||||
// TODO : precompute the mask in the ring context
|
||||
// randInt generates a random uint64 value of given length
|
||||
func randInt64(mask uint64) uint64 {
|
||||
|
||||
// generate random 8 bytes
|
||||
randomBytes := make([]byte, 8)
|
||||
_, err := rand.Read(randomBytes)
|
||||
if err != nil {
|
||||
panic("crypto rand error")
|
||||
}
|
||||
|
||||
// convert 8 bytes to a uint64
|
||||
randomUint64 := binary.BigEndian.Uint64(randomBytes)
|
||||
|
||||
// return required bits
|
||||
return mask & randomUint64
|
||||
}
|
||||
4
ring/test_data/test_pol_60__128_2
Normal file
4
ring/test_data/test_pol_60__128_2
Normal file
@@ -0,0 +1,4 @@
|
||||
128
|
||||
576460752303439873 576460752303702017
|
||||
97732016371625438 90768199974818125 23595849830835302 478885422499237042 108996286465591924 475187600246601432 491862716203655119 159494203428590386 86298953356657350 562114463189719728 200463004724829630 523789537205137887 358995880112345509 483181203531047114 270633690098963155 354018226577377124 457293484161180612 4615070116282965 89459508929019723 47424445852716043 90594396247637010 220111823443415078 257662573392555331 502494312437583514 239879529475689626 573425983720437055 516328497942190233 228663585981915908 31044209238476914 103470471392535057 511388702304518149 368899608972931801 145476378422114825 487262323843288386 107904745054496760 88055034521401925 56585434150885177 196640462806491624 136389981623754630 429337945796009696 368859988541736714 430274662842064152 187928167741063748 515688314389444158 403417439106566136 551094781411023532 323717266029565895 558937870392389567 471754223137848230 41053112627320707 280533529583595517 513722745774380872 122792603074984110 46622279786089013 307230109495809753 59398079011321018 96457491398020385 522373512965930643 8560103407636529 399697130543641477 163636408069114136 270181995836089240 470799398781980823 275862023179614714 352934896842508278 76973525847723882 145264024520135017 513578871346663476 207519258128969955 180610806482892131 461696011787411799 313495326350009735 455144377938354572 125456045208300616 119966309744057302 164454584908665862 331495774348203429 503156457433729559 224062317175947469 567379598288969077 135959695035619135 407153599237326557 198495743808852847 113534930252141126 343789218154206875 3536564937496768 37424743627994872 185027368201141995 155102784974317747 191680471691569560 346628585379348841 478656761196971099 139118882313817063 522846289453610841 492511851016521522 555208706527151560 410495078507399525 448119356082867571 99933424485220448 18602605096800085 60813036047339118 241899471610186315 508576447179129535 464311473803216558 376985485353552299 239126669625602378 484890106499930913 94939585375821378 80566418815363468 490783670982964035 202632215947649374 514965375573062123 531658123827987081 398194612767608601 167284358022337077 531200074879119802 439500922768541044 42776946772722161 433950184511881293 557187760642244054 367933961962701903 151252559982192845 64408658886973264 165626879680944478 365121108911502794 1552455093220708 312347871244871475 347988306135829908
|
||||
398325085722957575 329775632531456153 419176454810781333 259937617551217697 168500600223530210 151991690267387269 108860511285852494 45741234805662376 139917031016975860 524887574760494778 456240251042665404 357023454064000667 485419448343485916 76854250369626110 138909574696165490 428300086221527047 206522109314116153 416925041524789351 402338246510218858 39806089199464004 527614768682258248 574893639685684494 500993191228169112 127983788845553249 440445520034505118 74475689070015151 185211026392384160 78934254197671055 279682947346739718 459087668183506315 257522726248787837 85291729968626743 534585784542715713 208501964419456912 332491554969316625 101721118577979452 77664248727406705 184164738988359648 199710223874186074 375497967959109926 179420421015350027 347007106866446799 104358682824513902 285605186360092113 397873432062930046 350037900669692725 159359547494754313 199729241503021082 270069020491584608 420621341744767039 269150993153950854 207250053606859043 388553805955139286 387186932455512145 375209872382342182 161757868733703791 83288241797297825 430781647438061446 193565764711147478 331750101095272425 270533223103528663 246009907947098927 343596153028940734 325898328206707924 526485725493468223 172528870139112397 148568946473212136 118895199068665142 322183228352808472 271896751765022794 364251788081298995 534166364914271936 571618495915067346 463812786889394282 518524875893781751 225131790231435031 230023644297893272 554198378733268210 341712025345093378 212897004176108418 535697298396097846 575062050199044406 404250801270051723 402057744956922363 449356218260922361 333032020782675401 264784053187607519 535989260425479141 538613991494063131 248707100973686405 29483832982946595 50678302117586245 43263373547418327 162310563421216118 95549268923304352 464518846394694345 568796153451158330 499148699826992835 145821429333245536 192103152734448584 462547665762975217 429060964353116857 360409893865808917 451593016220747239 428362680887466034 41562968920252920 371921593701190324 127075237563276843 332550010392063718 279653483682341866 88936091802481033 435718773071155969 131566660099340997 539543265431320625 457822377013041147 573431249779504794 46774508266591229 470110573782201612 242443964863556512 160533015839175984 536768099298381324 243520971791183842 97485067228223196 368135663894970265 397912296323528731 141091266428462082 226544164367975752 184962850955815430
|
||||
4
ring/test_data/test_pol_60__256_2
Normal file
4
ring/test_data/test_pol_60__256_2
Normal file
@@ -0,0 +1,4 @@
|
||||
256
|
||||
576460752303439873 576460752303702017
|
||||
42095160184191000 109101595944152791 490530386447891500 171393827246485763 110066244758193925 413440073288790893 253681535583379831 511102234531820997 106435434329997370 183403433702896376 311359441342055641 221719924066175751 505010381164697913 38455312130442060 281909799692314474 402305504287088226 500164147660483358 414314304330256017 132065090934975693 404346546548112940 158409908441754836 433457066568118999 141755316783727143 282541307859821168 224917229807049984 290631930283612638 272532647916209017 138458337514237703 354181256135944589 175208090049028319 482027769823559570 223188243069432430 342635721857832851 224091616177813417 453357531624640918 321102614631377362 254890405696764061 415542557926396570 568360094162080701 144890852887622912 395843700531424678 446257592060263881 285666389722628531 74216204189607313 354597507719852127 59365746891320294 136783141570697408 317721434520531089 143270676462505953 464765483621648927 576100813526367849 428897244487554806 555202077615358328 1118640504721798 447441771294283992 373514785797509738 68260550619114810 101055759353303002 168695834573790944 296078415630821900 375109959789366892 49120763705053158 119138071806041863 446275089288716164 477235143992470955 41140098073621558 219575705399502446 143115084384016282 414091277018708128 42907353338498586 214857307631643856 390861284062543671 505723008283008911 34718168101536049 221294945918905446 16480829279690232 340050113253715474 297527848625908756 403134122946882893 82925442740832597 218001989160574916 72181603721849961 469692366598574494 114691768354584879 169087336081420619 377543756453981149 76114442184171873 32614552908826520 292986841750829378 400553556847944265 561202132487905836 39502044093572447 453485916966755059 370519733979222474 391909390346229646 290789750336523877 239674582921592825 58773791812475502 244726911467017287 172632505562997584 162471182882668503 199313229952675728 270090408962296077 110806856688729838 130042004855178137 149575204127098828 504010106716522724 532033355825339464 434748323387128334 150925693127442121 84185731522507367 129444981333730569 378582347355952974 327999288851923860 271141701027232697 151548415894965517 52042318852554145 39572856504735093 324819094404321437 320425818788696121 149668269633022161 223914593491690507 75516351444637887 495423309708630673 482266571176917986 256725859922264266 545312652490136114 427931270165449918 269546602914647900 231294584508865490 477908582353219179 451007695513934983 170761942014601681 38769511583578705 465059377903516857 399494122252914730 418566400189546569 452421231121962208 269769793790549794 479550566668440029 305098899397494455 499345041000781302 544933826734100820 75127817669661127 364548385491999782 128061976515363153 285468625521793188 105151831678752224 187847280420256196 298890141588403066 494477757230600967 576271026411553984 396802250316269560 161417424164405155 369356761514252968 393303600092423304 481316118790208624 139884366159272722 275416529581728349 267353162828239771 302172837522223228 293833235014017326 43240964572265380 383076704502018817 116582168312681937 461936290145643571 498407564943578062 224332901212239694 46041774682936653 504966370988506014 435030051955717661 406909309228098464 38004516362021717 159486942099202560 282489967857058119 343698342810914671 545049917977963325 202915328715475754 139708103671758502 194686971420973342 423240869540423291 59658536488735488 2173743062549785 438988899357490895 460642788622320370 336524309568430599 438966169609715032 415102773586753626 308742914778230283 536290974484555410 162447487779786800 260642931312522096 381630136254177076 247318909606758737 157162909883115922 542183189837652739 191363918036388022 511421578978915816 155289566189272746 474643826816753309 282384869793380335 303495759249360883 544086828773329727 223609247280629081 179938137573415822 73454685303433194 423300613036699254 264566591005031818 438669694391669160 458812077765198307 197987594379189501 531493751250075326 358592844839950556 452956070736604204 192891297407597414 103642895263710177 73357442156405111 511442708062650304 112431036110854468 253712893432734789 281333891072346804 481379892629981665 340113313507355936 561605362196202798 399684792219746040 184346988374227074 249508322266560187 546155683122114420 389361249960108326 512092961001210228 406247585968781480 111659389464777842 451513713237682854 256380466618677357 483200397019642190 15836568063995494 4743510619301532 550773698467534918 203117385120991553 441977035355301742 344073917441478448 430232310037595356 372259494064077314 51174529221651528 259011216293149348 167685132132967610 205634545095293698 521208430360029185 247714295723670540 215181531976043968 295152622066067294 91537131024755956 433585203463688765 427545441130862653 421241715290760485 49292291716570307
|
||||
438374079923311408 151871225000496280 165490415952193415 522881568787105855 36374894333704923 269982211477284085 88517474106497880 515153623835117885 733130356621373 522805170603975632 107078018493783679 170805418557284752 138423831533518810 11034275853485810 11233467300215081 422885813851017592 423947764850803718 167390123076436879 235377630525523241 42004027801445773 136144817282271289 470352744814720880 251060196273723094 467298502067495119 268519609438488785 119599681706482127 353490853305360867 38289179009319859 385846258549538228 392342244944969068 314658921800917237 420918755451390776 178855629118933307 569355817429455235 273806823343357993 316680332101990374 500079278160820228 108457819994853199 537863397798939048 79361762498464675 63162502763174308 16283885757587140 507298692262438380 523659720536621853 98417649894355803 106474132144814944 455397768304575748 457042069241188378 388370019102546906 220803563888461490 333150349836751502 228407529700780707 208642537155428790 407329668459419432 520696492869119053 445095301460633809 542703106475933867 214936712509960888 163082455286029846 443442316550747828 256820606313500140 183779793576925130 366196012280169192 363229854560350969 107477986206315830 251477541394054972 111236039976311875 285145169153116235 554652150268589124 72687953537031470 486812332379737643 148681257217061898 152064031653944667 286309440252081212 405633493567400926 457688641338310689 276341392127817855 185302683967219110 225622054839114028 216395984228346698 190175892719312110 122428859679701124 40882151520859909 264981204486729686 458614000867786793 364990983485586793 428081893773536975 220069494819961279 168885902294474096 86078230691140650 325228274286538712 358007621955930 399555289040732189 297884936626978455 168234425609076513 265384114349611699 126934854461956946 112121012707107665 225440203209834333 136028286584260516 230525319375803967 150972772427181231 22208097738400617 281762400222670220 436622750040836967 333898151588389667 99056287217213919 563093741248231929 337060085442606637 281788951773006014 134731035525600259 535656922686363283 136491233346242323 535320754798296843 237265923006068074 426830010752826876 305723647276639232 150722151409138685 97089744439964852 496927154968869817 400102703139245285 132822370587985550 366025949131468290 531495565238433533 71164889923959401 166427098759126682 117070099686703019 293773064870361263 573417900647239041 407308643121250375 369960143573050718 536116434056842074 80841203252698886 401054811478765343 474758682648269165 530626897040482187 352061327377598790 403671262828650487 158151377948777897 553713350004834331 417923400425827185 316567146170698076 12576508386705328 357480477764326584 29112903284401295 107629048775537217 525393354158079036 214537399568531046 167658412364557630 321648389321312353 469366305788064601 33407383718738610 440262400626763643 209672037072956107 64908494015551581 110567275144257239 357216922555514710 229667147816038358 247282547492043835 111719371911355255 95253480903670755 333186733358808993 239393651253448537 145417273422324014 148362193019605513 370833859151328863 407002300064570532 131087043323355832 53849312492062398 481830700823584990 258536652743636015 420397979939671260 347665750812598169 543696561907371157 512334250516405603 308164065166453277 119012028433735704 551883964943518273 182935178261089087 238841136170274915 88333507146103821 331758579405713647 345685851194730117 136454258722123543 406097219592408783 437318349362164895 411505921450126339 333343310848662971 35994437704538912 55865647364230075 478339409437900214 108077472941608600 82714472134664508 518432368345935435 447765746559718468 395443194116628701 446622261967094684 53684991760284855 531998427286233184 277279719584419176 528098262174261941 118358852234620113 283543211158663639 561165515612370208 172594565512727479 262686943116580648 44055602050243476 76350781930416594 131179299042115405 566519337177013965 99266541098759746 131325658890483070 154975066658623313 472783319139290132 238731997419179272 241460952476788274 174504811499336743 234550099979288246 322204355955622110 78934200094653384 254498991004487279 318178349068844855 139159150976171248 270732342221664832 31904671983518729 1100745834824478 357462621593724734 30593891889121841 430506947873667140 171304948801765569 313155301989235709 520489190168724055 95377385270785471 97680931624385533 435475457795949562 573747055445900046 292756317642577656 306583520152022872 405880325977863201 278751083849767849 523204570955123616 547636268381344087 196942936787428169 279923994922621153 399554591066591782 322120175749227817 218068820571191915 298598319400913252 412281257441713547 402157633807623434 128308786147750049 363702136535218999 100779271334970249 16619101977999813 341889113861022873 150595312585620805
|
||||
4
ring/test_data/test_pol_60__512_2
Normal file
4
ring/test_data/test_pol_60__512_2
Normal file
File diff suppressed because one or more lines are too long
4
ring/test_data/test_pol_60___16_2
Normal file
4
ring/test_data/test_pol_60___16_2
Normal file
@@ -0,0 +1,4 @@
|
||||
16
|
||||
576460752303439873 576460752303702017
|
||||
29335002291498019 74733314878908829 345757914625392883 424592696763883150 305098757618029540 315880659253740539 566291353020324899 381879490285643315 34642655966258078 436368737741273744 422320479487058982 251503834452711492 379754966293786644 266993967580766257 265441209649369663 479048496297441983
|
||||
229005636957624603 39991394218169426 168047666046761487 148360907414915405 73259769245767872 16981974422312794 496977853225992141 166066041724987771 264052080009592093 298274702686123828 35777507392976624 357559017452722394 314515717429384298 162821044855043426 109977030677147798 81303063671114932
|
||||
4
ring/test_data/test_pol_60___32_2
Normal file
4
ring/test_data/test_pol_60___32_2
Normal file
@@ -0,0 +1,4 @@
|
||||
32
|
||||
576460752303439873 576460752303702017
|
||||
446676853741266417 411151928268544268 316113499321051454 27913108070624651 51540830435645164 521237542860943234 101357399788904570 131954578061054846 426126842924748251 418549260400713113 16929507722000238 412590707346441087 343413419380971676 78123437644360389 30202291605923289 329950404030012174 45809159977851154 292606195202689259 268750103924286497 568368279163389962 560909223127878875 558588607179710396 493655028901461669 414111978138777740 278535078066275616 113588009827879193 209261052212448452 353135346479001399 346341023042671234 483982790455356668 119949406999259397 254260032891895980
|
||||
143927002157429972 24687919550176982 314055826394969007 189484637018701066 313366156770460233 178292577188569981 542374777815210606 223556795824542649 223980592075583470 423163811223366723 99190341137476711 272695567426262689 266242884542649103 358056736827572199 506440945724186274 334549312617977133 60514885744437720 349916159272998893 91437024533871091 338072583033829561 542244024826568584 363246992092632200 282873928030797178 160788901878102755 254652546645801685 71233877720226874 469157444405012905 541544586457299924 220088038037539754 478604268230087801 70363296523078985 551543086249836966
|
||||
4
ring/test_data/test_pol_60___64_2
Normal file
4
ring/test_data/test_pol_60___64_2
Normal file
@@ -0,0 +1,4 @@
|
||||
64
|
||||
576460752303439873 576460752303702017
|
||||
262736013155910555 134399205275389356 21914580535790772 345426000281969043 251565806300980784 545370777294757504 456789672662601734 420510177617190772 520650099498412352 53342176101504322 266011788449623707 503030216973029469 480930369980293997 321987454665202318 466721383455395734 273836137940657795 409636357248453562 433469171519178997 320344646407259980 141246220203596710 344797697712039737 504331654488444275 539202700550645523 186179085054939372 562602814568645298 543444580531283077 160169461121173935 350784691042899162 32678121466372997 569786794724914756 256355426620994401 3484126615551694 405840730157601369 376838154071216457 373508366771649401 124731802589699282 71094821924776811 306103433799179447 175750785469731641 65474140500066740 371084983783298888 18142029106380172 329736515853421422 132480713678162489 221251451891618621 4310502425227271 363433004803519551 65796095961889023 384438118323192470 274546334934457714 290850422752767846 57088190015495864 40220816835480310 568564503356230570 231229810660195894 81629682680720432 522733560147139162 98603219285448603 83840849230837754 549213886521809048 111942201345539170 187981118119470865 505358403753068879 449509564212143658
|
||||
315563096049493706 286332252766718888 157584939926698546 188556064680622140 362346978677543649 33141704184747042 466278349989829991 217680314197813676 433045295628943700 54643309984639923 520927393042275616 494539823213582711 534074936279609670 30356247676684042 390039321385674108 558936758351380586 374424348267536751 333003601211472366 492094016058380509 489969109220547235 518904961471759346 542040069155845363 533783285422810649 528578706503018303 79313562296466244 57124514167542590 568476751311349902 556687943355501029 154784346549824067 343793100609373579 224113348415193184 122576507003655459 259944454834590834 130015234738825441 523596193693605695 284717290862492787 368997453644200803 204076026471479293 539397747320010409 419921142963716925 552874859521723465 279937732415513261 72857419145886547 146595529257037525 196777875321712164 518476909977358962 290724912693894122 359188212216346799 449236428207562273 320023205841552252 261698759369002521 427713683951239679 387729587142487162 153540267215424145 247037912180918548 100686811633196283 246517550529399413 447008318598530981 222485032549971087 524469457919726638 118421467808057284 354531050174351229 173072752611467865 252333998483157087
|
||||
4
ring/test_data/test_pol_60____8_2
Normal file
4
ring/test_data/test_pol_60____8_2
Normal file
@@ -0,0 +1,4 @@
|
||||
8
|
||||
576460752303439873 576460752303702017
|
||||
218919769016417135 285383958099531571 154130792340322329 208160061956504958 240497969120867784 56870045954269445 426823474235379995 504896482300062811
|
||||
275112135024930041 250984234238255359 362910580529173626 199635459264466796 384027850586118086 24808063873304527 338428138896886890 278051156238693569
|
||||
6
ring/test_data/test_pol_NTT_30__512_4
Normal file
6
ring/test_data/test_pol_NTT_30__512_4
Normal file
File diff suppressed because one or more lines are too long
4
ring/test_data/test_pol_NTT_60__128_2
Normal file
4
ring/test_data/test_pol_NTT_60__128_2
Normal file
@@ -0,0 +1,4 @@
|
||||
128
|
||||
576460752303439873 576460752303702017
|
||||
420394463054031650 569564913731132411 7057936446468550 474894849814977477 104678765359006719 63897100090302365 84121548734801645 29071657980539859 312596562485814435 412937401936786180 255257031480403127 7954441083802149 383137992395740056 263179780928838968 71559693313454193 241150603986790194 112021833841841863 402837912814282410 163195346721764893 339922115031537058 212981876804802784 272484675678019595 404139696441572034 238170859930359182 265087401475289832 391654177782298160 55829892839968113 11083746841596170 477308324356115308 568054672469371605 36532226228264539 313725744411706325 9205398466664202 554914639381349993 273406334607418338 285270414346177715 77400150553269002 448037320165398537 398904348730917196 542238686444242620 424754247816805340 351483429648832946 268732552757971248 250858329812420953 429317269468409603 357637259336138504 123440164854999304 412723441100850157 414183923232445449 369129588506345250 220206638297796406 411773441140903109 142859436910095988 363257751306364036 423763047801616368 413455954860582187 26168831060195759 156430718382048772 116862499252544339 256516924193897994 432715869016470822 400902550031355359 435553003688250244 499632169879153552 485312530067933633 199828651328794629 115599539431833135 155740454982452370 496837040069892246 26178608757790613 313075946181464189 240731251011491927 122895835658026575 414309979961717300 312515917992525827 155868573432355125 138411469573519916 232922453352193395 335537085375139194 92565317781012948 334301378788565569 76053694488653081 438479195569076226 176428169858642714 175654452412013639 302142274752911669 462766248076079193 40892045918643330 79945034714230644 500232219493329437 226789253246325774 208357051761240693 527523756193329312 259517406028706401 445806625286133944 162461403807387406 306958040428516002 473734267232060231 369953297613195627 460452828881732036 569521811633374454 23392459013483784 367551559650156239 561330873032173980 227465568538238479 10740125677661565 279503700143722802 216362260817857472 569252656743366550 75142729955336655 390695696714765580 393322591120964327 200428133408090059 420909031056172921 249590947554721395 151404599367306180 330502270882464896 443897791820404714 475930689244570144 65631591225342649 255812264546586573 330817802134085624 161146042895115481 242176965644128670 89312118193433621 467015686828527150 458242814111589255 568948029306362420
|
||||
191355180928732030 405357855540537711 472927423077770114 549874186985995240 326823672950218846 155973286068119857 408724741674811938 172208815389299773 423805038662923104 333492957710024622 554486910827107859 127188220592734687 531323916087995009 252077847248100239 99987234324021569 37191920143169163 82937957257410595 121825521269906453 339720235218275102 82789691138534154 425678228162255303 494256497063916840 219582791064837858 9559459273209693 177337141404602187 379331609069569764 107093807891530473 119523163322577748 459581307420743870 282148383829631456 344343045771611716 24166687307241327 37316153013415913 542011859596250179 206329854132876090 483596897261725805 494598841991799896 100225529614506735 556652301184968611 262533079300114250 165762036858858306 283282416185982281 48917092271879162 153594204595882408 164999600818396832 99781589091822615 568067300891789921 212231385931676268 465760063245818847 384695568870808781 275592609453711831 285490744001541593 284493524356424200 481275463997528269 64511424442958191 219978603493132882 450671120569820905 538946822064907493 337304810634702201 426112725187050881 338627112439947447 236150737669507353 357853806256580240 548273148624116717 487275573354804641 260851638257950504 247163476136898923 106461829485094150 169412788497852852 282631340341724567 122221750848179066 368358750009096263 250069651722932461 197763641174247023 427702227431958631 210420618628839161 322428844515049129 263186465048597744 343588880726368135 54678492781491008 293657697519745641 236902581815693581 183205458128341716 495581739903641563 472828323354088111 477996537264977452 532879355473615148 64191215950082819 24432169963705807 249741571578066401 7216087568430740 301372045319276471 180182075657619845 2899796465083139 55792268823198307 377657792165889326 441573275497649103 535471908346744537 156753996238540302 508732520354600520 263725942421718348 423484844600235916 321747420070707273 326325949676532560 306120346771484630 432933829874452142 230155096410032141 70826888908207334 210386294609771016 419311966073912181 353568115339419853 413292013674492880 38192400669035339 504814848704775633 440796553634633412 296473450641927044 428244252966201208 376856794738996291 232567180260004555 342068816828263509 10335916813882108 407606833092021190 472964373757334560 464189013609431132 128203702699855167 396702136759435423 535122256056664571 378398880812001603
|
||||
4
ring/test_data/test_pol_NTT_60__256_2
Normal file
4
ring/test_data/test_pol_NTT_60__256_2
Normal file
@@ -0,0 +1,4 @@
|
||||
256
|
||||
576460752303439873 576460752303702017
|
||||
25539180957916247 134576910680253174 475363799372172620 28098986814455855 274716161371720394 312179856793930138 164377263132000736 142008666615288623 182735566456326871 10866356083886021 208090517816132918 52878905204697439 91648973731241697 574991989957693552 500536710584824592 358944371207232906 523477132322162594 548193187974410427 235886312841325678 150731728017218454 281797201443117083 563294233426118731 289732758013117188 570620768676577299 398615831645796041 97428788315228353 102871409071546815 532774000551509196 360827243328873706 535362656854269158 535059407809998454 241346422338253426 24536225847950339 187719898399036285 208087398158017137 549455574829620242 228473756573465231 125101592748151531 485669259184335899 314907593061345725 261963958161490446 525546180420182151 22554511326833137 376466522385895002 369473114047926329 303203901068497041 574674676229664439 527504934235112472 168298047449962932 6959731275881451 301905062208822778 35729762669654407 20493061307934269 131432970048788868 520631529780789195 301752544003086126 516394549566453450 161796946742173945 363730488537718291 10381192361222532 257478649793918421 460797117008135956 239533633719433201 393571089242275604 3025580076213915 564788969263356003 52926550336486024 385158042964444234 558404729092018644 273273984521467187 336829236901536149 136259161339784794 165972191001738739 471195471629941990 186627813815902895 456559165377063043 166026756416732478 188754579842951634 507086289889700319 240511770516592994 572903766245421175 220419217563396790 79394335226244850 177831146025370705 543848533539555140 434815340316821821 518688466567666280 391830584516654655 30933794264219615 161405910933666617 113452875623048931 313941012128973161 340599144874841662 111666143698454306 231072651334712398 155616342986526411 385563544154691802 244056537020624835 487068683308690119 36993212127766784 502465771527461487 142390985028631718 126906895255600475 261239512512479410 21161876464410161 187947661511159557 233535577934386038 312138394777933399 110643166062619700 145206386746299120 553028679425068984 140706971970880894 407939191362266539 37289166785375282 508157176141245827 551706527909310995 186458252254574880 128706071973384520 182994710910888687 411552050321037567 547771777360445370 404363457914024452 70844599449300401 217316763159908291 513423675799578835 79684810495498019 486613676201445617 41145886242735629 244328374970552507 555498041402474309 193097096277950439 193322820485223642 278098841963886377 446088133563104331 150368262197327810 57814225182172893 398900050623878621 182682427176814874 92318944605526269 492708910209069566 420268440336995572 145657280705904455 343314692203719814 307422559616350551 44164989486021902 214443534430470015 296999464537290595 43462846506271095 216208877773345992 563303440845370321 348258372473146442 397062819065434969 153146538498376426 254290911314356679 132001547094349104 364547972914370422 19707992960332453 140039763791528979 556377762570493749 402149051732693816 140253667944514421 337563373862946670 374978005797593455 189126997987002783 417283864907300551 136506305103680265 175982684712968603 547282725480307214 216604131378799933 301976393125085872 221095440783864307 433607819548180555 447740292619155239 403534477140159291 405040738050507824 3415154835862812 143391176700890182 286719766792058075 53303431082522763 31901773118684527 232475810483024708 384962764956909578 47050371056891006 494242028238355208 199516451799148501 286660269856407413 144867723385532441 305631527286204929 564731806991992075 123358856332573195 307667212210298256 293075170888354570 174908206112234882 129089751003290360 474375508153621337 183608558781373932 444232557414546029 263358914639985593 271259612067651959 324488002261909057 525442980499421281 342722666680451556 461946597276054625 271762493639233455 290502006389591490 313211662042179852 12257852953623893 18787810968673210 125914252484836784 189437221511680189 400657183087768110 311040266109793939 228204108229580419 149782056579785096 162526192005070423 398015975429587692 253216106124630717 329756581376514000 225447746805029464 156966782898480045 486360406929135337 198540927585903828 445404412810388420 232006240862884241 447700714943003583 224634965652784343 410634304584122048 512823303344584600 130972449347622764 431618391706465191 400658951067291848 389050390523422608 26121738213936139 497382085969742655 77565734253027774 493536528715434320 244029356101575008 197760024591534648 169810260685743310 413572371974577702 44943371344227053 342037367697811921 574608314263527686 491240089951929483 74820066611494113 205738823101341462 211835392589657488 185392954748001361 491682849059049131 282290383290792071 238680569454837425 489904800548920901 439977546850826561 162263651720776212 232613675637076929 9824498340588603
|
||||
145262921017258530 509093843511663073 69280768158594495 476975569887795922 200578418001088989 511954582967998215 81004975188137317 434563516464118473 3742487127533537 148853904131153735 74250494922324744 342102325202151178 216224407082221091 293496062152831898 136490810673202468 339428511849083731 104048513922313017 475425927213645945 488518960243328468 492132661995355315 568869884521887731 525754896308909538 308757899760222748 543052554698889604 127011667295243721 143951070705256545 333924117897825325 468824765157795015 536375648731223460 500143443529480066 378489507927363322 279125852150864901 281030498504685879 168745392570902508 539413237674932832 250602279388059960 200993615157345610 92881688160082790 475445572000753331 426044792013466179 336290989081788700 475122233276581982 571860618885320337 100964993358312625 372984491119389868 344569715565410746 60242051153217051 88469947602760817 371439217449124235 82865000924762212 533773493545592846 283571462528268311 8050888500210530 548686786463874881 500956635065080289 206216378852994063 216220061258450222 18289119905402672 520780087101851718 458244492867167112 477330924013911956 27986645549973413 398017447689976420 72499293099358184 15661124530407527 436483150471732051 105655592136018165 250794382657150445 502230204109251559 15902090664674169 24128985185766359 576339228734293097 380943101080030564 317679187729245422 291821169074489762 263753517262679069 500813118120459206 5907313506942712 568798513863161214 568006665966008790 463900981809306760 569323025022789054 531030207503205866 548067340028132440 216123285147297852 95879880056442795 228011300554486051 5622793593072754 162958380973089483 144297612561770555 503945874230219663 510213895744613944 427370607720726159 485146245914886276 512916632692748009 125006578501399088 291231141910373887 34629924131612135 172483273858493350 191078353494166299 337889874121201580 39069074983148686 43844758560188396 274933702252441871 212961990507164718 31952093639076407 38148544443399351 81177593602577738 573173862197834520 147438384745255134 522225120977990761 128293858134848977 179376240238201377 229390590519503455 448982237341985904 428431412426973447 444523050934342371 161540441021111816 344327418634019499 538632796364184769 286241785850763117 540885238416105908 199039391238087947 398173855569002898 443657369837614220 521436485927088249 350958660678604668 531550026478083390 292329282653892802 249865445848442004 334844977362494761 253735217175168864 136440994953492269 347988625988330869 78855725766197923 6886427804107858 50323489907205385 58723685908139964 363721068166739517 115361456607105021 430865188649593152 306506558745397883 16347848324091673 554960316053659212 545770074143278266 33128127278684866 105772739927473782 48870139210473549 58794748087836674 474712978371384419 19565336952072949 545102013246715960 284794162160562258 534046768243604871 515139885978567640 75943870618151495 284478323301965809 211393418778887787 489917114612131538 188837273741590513 543734395403836874 296728025656446498 255513679081425658 140034757922212665 125105027344710076 376805120950157646 487762543330067162 73771023075742516 263612713835667176 70916292187027797 521041078726186979 63072561940441183 391041648541819815 561191955917166645 484383301426882619 524939240614839600 467905925305918463 106615952246108837 283645178526878518 28844359403488479 147243109816115707 135557950651844021 114531164825658671 328546338419561676 343928718523665033 265711254649598556 70610661045066165 427057141155416534 276055042511001360 231708851502383818 295639172412693425 287750111926231800 406212266070789998 36740714235231076 474250293682940522 435581687227388193 196309786272732675 507185714491123044 87854256637724473 520719983214989992 309761945023637181 512005901530248566 49779398819269784 382269384917273187 168582591020161001 545252104333691897 496452821607793877 416516492420337001 340944168282202371 408719995683740029 456885247723827471 338637400820834302 451239483358210867 56871254144936084 561207652586726183 8053350254065332 334280587965584003 327702914397759466 87572048046481558 490378938312633310 495270055649375258 33600534065660095 331477468756018874 144608221167985876 284139694548925586 314889468034604849 416733161198210068 159979018438447742 239314906816503442 141394866156749784 8215297667275886 144926935976350507 475011371483347491 530765618252712380 17739432276654581 228304617638032389 75037080049003521 528668068991034981 144850219018031660 237897487839865383 216386763675580863 46440500047230549 287813643373639666 400047078833455375 387196340896419108 306127222070311235 134096648470839873 450899972818645458 116213931387898665 283965153430444480 131430617068404218 390867652280397264 58206120712600131 5314128460251204 417802652644041302 464476082998386550
|
||||
4
ring/test_data/test_pol_NTT_60__512_2
Normal file
4
ring/test_data/test_pol_NTT_60__512_2
Normal file
File diff suppressed because one or more lines are too long
4
ring/test_data/test_pol_NTT_60___16_2
Normal file
4
ring/test_data/test_pol_NTT_60___16_2
Normal file
@@ -0,0 +1,4 @@
|
||||
16
|
||||
576460752303439873 576460752303702017
|
||||
478709994917861263 384523361984839039 85280178929118517 97236771105538581 405398446277957930 212032954159995430 422470404160315474 554803939008707088 548834797847219388 77555291080479046 395019082584063204 199181437220481637 117237287301343342 288680759037675256 399758453229973389 414322896245918704
|
||||
48052203194603178 560437377430510021 51924270083317129 254030332439706305 520426933791709415 443676955646482348 405741025864202685 70579349438930370 187051495725458514 84142641467084820 194371127241444851 191269223870154261 109044160236534164 304031719544775780 243823945337031160 571948182313750664
|
||||
4
ring/test_data/test_pol_NTT_60___32_2
Normal file
4
ring/test_data/test_pol_NTT_60___32_2
Normal file
@@ -0,0 +1,4 @@
|
||||
32
|
||||
576460752303439873 576460752303702017
|
||||
137060663770328093 375023471258971655 544605838678798786 171413387990566357 251152313881280483 732940359141970 248105265573021143 375764270042034794 334418511524926027 409224254943060001 531835442854955749 268053902549857631 472427523610083482 513001774296219269 89272726349069419 341799844389716427 452664419230461269 475846714013328459 23638687787168199 563679077257994351 501913295240650091 201362599267133459 134655194250590929 539789510912220196 559584782042897252 391776092055273537 479853685312671506 531912061345838428 310897563741463711 430304163842393712 536402798438763190 213182781392446404
|
||||
385609543039092107 98729129892941648 329153938426401810 160953615178476141 151016379459627133 524736304031292540 465643194968706978 187115479287854957 391680866044038671 140834657643642928 574058782286598786 448304021418840978 209574484307591910 572532001944664625 172479804513191158 420091611466992599 119558459469039893 356435460777079045 108103374368876106 503743455397931477 69380493560432256 431530551369021053 186779901639661695 73454606420882002 213952214441851970 519290813869281302 470443363479802469 88580125424727240 251802327334165314 335123979831683196 206282586561789865 50374559611195388
|
||||
4
ring/test_data/test_pol_NTT_60___64_2
Normal file
4
ring/test_data/test_pol_NTT_60___64_2
Normal file
@@ -0,0 +1,4 @@
|
||||
64
|
||||
576460752303439873 576460752303702017
|
||||
321518699167648100 417881319568932369 230555884338172310 561831601230838020 62007425346769512 447092424612548431 512502140803857146 75621680689690000 382694839073952907 318607664233993930 483064334690838999 221096253615521839 280196160665220281 471847866388018856 131701726817409548 369959988834814323 288968454985367497 327076957002935454 88423739355957937 407565851335124222 555060644108399599 380495900643829618 160566237744776480 60778823305665464 357931449208419185 528807315243089409 533820948251252055 188157797621304948 133867446235985518 421573907993140047 178857864031357204 556262544877832945 536492340226343121 506894664446621918 576135288812969955 407347449908315924 111848763197334520 307173437158786090 116329383774254859 294490215051904836 236226507111899091 76501981671984199 429852729171957903 371178100003685567 412024164717997702 279335696499888758 427254685516918570 529789818950898592 238711537105549077 107378873938309514 99694397370517245 241162149171422311 545895879214808028 516323182030807189 149803985722268106 476650002159286016 179164621851181463 447940755549723717 78394092720640890 189503579058519682 272017066509510505 494627433185057558 353274121069186028 384517313201141544
|
||||
69861911001200639 143389998318318571 343625082217054353 7187136398219168 396831517601705732 152375071740746717 395864994503611269 264219981008901846 334124939535910642 11136803465188710 189522479437540624 258909730001412486 451619844826507525 52603901921495475 140112979349178546 166887826651010921 60494535967193849 522630044587800175 445249572480018005 496866786422545760 142192489017116616 57224027687618832 543545371816655579 182388660010474901 175934723809254852 465597801322691571 129531219899556545 102222958768734430 295370372940454186 390715973324513795 1105426387445339 102536906845185018 268388592020711618 572351706682694187 339297510726126351 456886671308123505 416822535270988929 46633807062075381 31298035199716340 163416866941300722 234121726310952657 77007562713851313 219264019724753957 377512342278490701 555517589494354969 314128337943076429 566072226659696563 223815419652912371 419004177092870472 450393143683136850 14799555274469005 496596709406778389 337341506742711794 296704116716776470 441263880478669428 135749193445630877 313404701892415617 2883423790615640 328569093894954878 473825634302423967 192163137798299897 122493010573834389 487186504536045891 446940576764364865
|
||||
4
ring/test_data/test_pol_NTT_60____8_2
Normal file
4
ring/test_data/test_pol_NTT_60____8_2
Normal file
@@ -0,0 +1,4 @@
|
||||
8
|
||||
576460752303439873 576460752303702017
|
||||
532040159151935360 6777376300449212 105015934674156327 435476759662080597 230332092648414310 312854922061405173 527557817676300694 177763842260035280
|
||||
133270078917385086 320664165956944128 162488447052015562 564362462797872461 574419935916152080 214479305361450751 457815076814980937 349858359686341340
|
||||
408
ring/utils.go
Normal file
408
ring/utils.go
Normal file
@@ -0,0 +1,408 @@
|
||||
package ring
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// Returns (x*2^n)%q where x is in montgomery form
|
||||
func PowerOf2(x, n, q, qInv uint64) (r uint64) {
|
||||
ahi, alo := x>>(64-n), x<<n
|
||||
R := alo * qInv
|
||||
H, _ := bits.Mul64(R, q)
|
||||
r = ahi - H + q
|
||||
if r >= q {
|
||||
r -= q
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//==============================
|
||||
//=== MODULAR EXPONENTIATION ===
|
||||
//==============================
|
||||
|
||||
// modexp performes the modular exponentiation x^e mod p,
|
||||
// x and p are required to be a most 64 bits to avoid an overflow.
|
||||
func modexp(x, e, p uint64) (result uint64) {
|
||||
params := BRedParams(p)
|
||||
result = 1
|
||||
for i := e; i > 0; i >>= 1 {
|
||||
if i&1 == 1 {
|
||||
result = BRed(result, x, p, params)
|
||||
}
|
||||
x = BRed(x, x, p, params)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func ModExp(x, e, p uint64) (result uint64) {
|
||||
params := BRedParams(p)
|
||||
result = 1
|
||||
for i := e; i > 0; i >>= 1 {
|
||||
if i&1 == 1 {
|
||||
result = BRed(result, x, p, params)
|
||||
}
|
||||
x = BRed(x, x, p, params)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// modexp performes the modular exponentiation x^e mod p,
|
||||
// where x is in montgomery form, and returns x^2 in
|
||||
// montgomery form
|
||||
func modexpMontgomery(x, e, q, qInv uint64, bredParams []uint64) (result uint64) {
|
||||
|
||||
result = MForm(1, q, bredParams)
|
||||
|
||||
for i := e; i > 0; i >>= 1 {
|
||||
if i&1 == 1 {
|
||||
result = MRed(result, x, q, qInv)
|
||||
}
|
||||
x = MRed(x, x, q, qInv)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
//===========================
|
||||
//=== BITREVERSE ===
|
||||
//===========================
|
||||
|
||||
// bitReverse calculates the bit-reverse index.
|
||||
// for example, given index=6 (110) and its bit-length bitLen=3, the indexReverse would be 3 (011)
|
||||
func bitReverse64(index, bitLen uint64) uint64 {
|
||||
return bits.Reverse64(index) >> (64 - bitLen)
|
||||
}
|
||||
|
||||
//===========================
|
||||
//=== GCD ===
|
||||
//===========================
|
||||
|
||||
func gcd(a, b uint64) uint64 {
|
||||
if a == 0 || b == 0 {
|
||||
return 0
|
||||
}
|
||||
for b != 0 {
|
||||
a, b = b, a%b
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func gcdInt64(a, b int64) int64 {
|
||||
if a == 0 || b == 0 {
|
||||
return 0
|
||||
}
|
||||
for b != 0 {
|
||||
a, b = b, a%b
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
//===========================
|
||||
//=== MILLER-RABIN ===
|
||||
//===========================
|
||||
|
||||
func IsPrime(num uint64) bool {
|
||||
|
||||
if num < 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, smallPrime := range smallPrimes {
|
||||
if num == smallPrime {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
for _, smallPrime := range smallPrimes {
|
||||
if num%smallPrime == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
s := num - 1
|
||||
k := 0
|
||||
for (s & 1) == 0 {
|
||||
s >>= 1
|
||||
k += 1
|
||||
}
|
||||
|
||||
bredParams := BRedParams(num)
|
||||
|
||||
for trial := 0; trial < 50; trial++ {
|
||||
b := randUniform(num - 1)
|
||||
x := modexp(b, s, num)
|
||||
if x != 1 {
|
||||
i := 0
|
||||
for x != num-1 {
|
||||
if i == k-1 {
|
||||
return false
|
||||
} else {
|
||||
i += 1
|
||||
x = BRed(x, x, num, bredParams)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Generates "n" primes of bitlen "bitLen", stuited for NTT with "N",
|
||||
// starting from the integer "start" (which must be 1 mod 2N) and increasing (true) / decreasing (false) order
|
||||
func GenerateNTTPrimes(N, start, n, bitLen uint64, sign bool) ([]uint64, error) {
|
||||
var x, v uint64
|
||||
|
||||
if uint64(bits.Len64(start)) != bitLen {
|
||||
return nil, errors.New("error : start != bitLen")
|
||||
}
|
||||
|
||||
v = N << 1
|
||||
if start != 0 {
|
||||
if start&((N<<1)-1) != 1 {
|
||||
return nil, errors.New("error : start != 1 mod 2*N")
|
||||
}
|
||||
x = start
|
||||
} else {
|
||||
x = v<<(bitLen-uint64(bits.Len64(v))) + 1
|
||||
}
|
||||
|
||||
primes := make([]uint64, n)
|
||||
|
||||
i := uint64(0)
|
||||
|
||||
for i < n {
|
||||
|
||||
// x gets out of the bitLen bound
|
||||
if uint64(bits.Len64(x)) != bitLen {
|
||||
return primes, nil
|
||||
}
|
||||
|
||||
if IsPrime(x) {
|
||||
primes[i] = x
|
||||
i += 1
|
||||
}
|
||||
|
||||
if sign {
|
||||
x += v
|
||||
} else {
|
||||
x -= v
|
||||
}
|
||||
}
|
||||
|
||||
return primes, nil
|
||||
}
|
||||
|
||||
//===========================
|
||||
//=== PRIMITIVE ROOT ===
|
||||
//===========================
|
||||
|
||||
// Computes one primitive root (the smallest) of for the given prime q
|
||||
func primitiveRoot(q uint64) uint64 {
|
||||
var tmp uint64
|
||||
var g uint64
|
||||
|
||||
notFoundPrimitiveRoot := true
|
||||
|
||||
factors := getFactors(q - 1) //Factors q-1, might be slow
|
||||
|
||||
g = 2
|
||||
|
||||
for notFoundPrimitiveRoot {
|
||||
g += 1
|
||||
for _, factor := range factors {
|
||||
tmp = (q - 1) / factor
|
||||
// if for any factor of q-1, g^(q-1)/factor = 1 mod q, g is not a primitive root
|
||||
if modexp(g, tmp, q) == 1 {
|
||||
notFoundPrimitiveRoot = true
|
||||
break
|
||||
}
|
||||
notFoundPrimitiveRoot = false
|
||||
}
|
||||
}
|
||||
return g
|
||||
}
|
||||
|
||||
//===========================================
|
||||
//===== POLLARD'S RHO FACTORIZATION =====
|
||||
//===========================================
|
||||
|
||||
// polynomialPollardsRho calculates x1^2 + c mod x2, and is used in factorizationPollardsRho
|
||||
func polynomialPollardsRho(x1, x2, c uint64) uint64 {
|
||||
|
||||
z := modexp(x1, 2, x2) // x1^2 mod x2
|
||||
z += c // (x1^2 mod x2) + 1
|
||||
z %= x2 // (x1^2 + 1) mod x2
|
||||
return z
|
||||
}
|
||||
|
||||
// factorizationPollardsRho realizes Pollard's Rho algorithm for fast prime factorization,
|
||||
// but this function only returns one factor a time
|
||||
func factorizationPollardsRho(m uint64) uint64 {
|
||||
var x, y, d, c uint64
|
||||
|
||||
// c is to change the ring used in Pollard's Rho algorithm,
|
||||
// Every time the algorithm fails to get a factor, increasing c to retry,
|
||||
// because Pollard's Rho algorithm sometimes will miss some small prime factors.
|
||||
for c = 1; c < 10; c++ {
|
||||
|
||||
x, y, d = 2, 2, 1
|
||||
|
||||
for d != 0 {
|
||||
|
||||
//Walk, walk and eventualy meet \o/
|
||||
x = polynomialPollardsRho(x, m, c)
|
||||
y = polynomialPollardsRho(polynomialPollardsRho(y, m, c), m, c)
|
||||
|
||||
if y > x {
|
||||
x, y = y, x
|
||||
}
|
||||
d = gcd(x-y, m)
|
||||
if d > 1 {
|
||||
return d
|
||||
}
|
||||
}
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// getFactors returns all the prime factors of m
|
||||
func getFactors(n uint64) []uint64 {
|
||||
var factor uint64
|
||||
var factors []uint64
|
||||
var m uint64
|
||||
m = n
|
||||
|
||||
// first, append small prime factors
|
||||
for i := range smallPrimes {
|
||||
smallPrime := smallPrimes[i]
|
||||
addFactor := false
|
||||
for m%smallPrime == 0 {
|
||||
m /= smallPrime
|
||||
addFactor = true
|
||||
}
|
||||
if addFactor {
|
||||
factors = append(factors, smallPrime)
|
||||
}
|
||||
}
|
||||
|
||||
if m == 1 {
|
||||
return factors
|
||||
}
|
||||
|
||||
// second, find other prime factors
|
||||
for {
|
||||
factor = factorizationPollardsRho(m)
|
||||
if factor == 0 {
|
||||
factors = append(factors, m)
|
||||
break
|
||||
}
|
||||
m /= factor
|
||||
if len(factors) > 0 && factor == factors[len(factors)-1] {
|
||||
continue
|
||||
}
|
||||
factors = append(factors, factor)
|
||||
}
|
||||
return factors
|
||||
}
|
||||
|
||||
var smallPrimes = []uint64{
|
||||
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
|
||||
73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173,
|
||||
179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281,
|
||||
283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409,
|
||||
419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541,
|
||||
547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659,
|
||||
661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809,
|
||||
811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941,
|
||||
947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069,
|
||||
1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223,
|
||||
1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373,
|
||||
1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511,
|
||||
1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657,
|
||||
1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811,
|
||||
1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987,
|
||||
1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129,
|
||||
2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287,
|
||||
2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423,
|
||||
2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617,
|
||||
2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741,
|
||||
2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903,
|
||||
2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079,
|
||||
3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181, 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257,
|
||||
3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413,
|
||||
3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, 3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571,
|
||||
3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643, 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727,
|
||||
3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821, 3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907,
|
||||
3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989, 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057,
|
||||
4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139, 4153, 4157, 4159, 4177, 4201, 4211, 4217, 4219, 4229, 4231,
|
||||
4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297, 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409,
|
||||
4421, 4423, 4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493, 4507, 4513, 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583,
|
||||
4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657, 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751,
|
||||
4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937,
|
||||
4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003, 5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087,
|
||||
5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179, 5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279,
|
||||
5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387, 5393, 5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443,
|
||||
5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521, 5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639,
|
||||
5641, 5647, 5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693, 5701, 5711, 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791,
|
||||
5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857, 5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939,
|
||||
5953, 5981, 5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053, 6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133,
|
||||
6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221, 6229, 6247, 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301,
|
||||
6311, 6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367, 6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473,
|
||||
6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571, 6577, 6581, 6599, 6607, 6619, 6637, 6653, 6659, 6661, 6673,
|
||||
6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, 6737, 6761, 6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833,
|
||||
6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917, 6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997,
|
||||
7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103, 7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207,
|
||||
7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 7283, 7297, 7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, 7393, 7411,
|
||||
7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499, 7507, 7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561,
|
||||
7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621, 7639, 7643, 7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723,
|
||||
7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829, 7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919,
|
||||
7927, 7933, 7937, 7949, 7951, 7963, 7993, 8009, 8011, 8017, 8039, 8053, 8059, 8069, 8081, 8087, 8089, 8093, 8101, 8111,
|
||||
8117, 8123, 8147, 8161, 8167, 8171, 8179, 8191, 8209, 8219, 8221, 8231, 8233, 8237, 8243, 8263, 8269, 8273, 8287, 8291,
|
||||
8293, 8297, 8311, 8317, 8329, 8353, 8363, 8369, 8377, 8387, 8389, 8419, 8423, 8429, 8431, 8443, 8447, 8461, 8467, 8501,
|
||||
8513, 8521, 8527, 8537, 8539, 8543, 8563, 8573, 8581, 8597, 8599, 8609, 8623, 8627, 8629, 8641, 8647, 8663, 8669, 8677,
|
||||
8681, 8689, 8693, 8699, 8707, 8713, 8719, 8731, 8737, 8741, 8747, 8753, 8761, 8779, 8783, 8803, 8807, 8819, 8821, 8831,
|
||||
8837, 8839, 8849, 8861, 8863, 8867, 8887, 8893, 8923, 8929, 8933, 8941, 8951, 8963, 8969, 8971, 8999, 9001, 9007, 9011,
|
||||
9013, 9029, 9041, 9043, 9049, 9059, 9067, 9091, 9103, 9109, 9127, 9133, 9137, 9151, 9157, 9161, 9173, 9181, 9187, 9199,
|
||||
9203, 9209, 9221, 9227, 9239, 9241, 9257, 9277, 9281, 9283, 9293, 9311, 9319, 9323, 9337, 9341, 9343, 9349, 9371, 9377,
|
||||
9391, 9397, 9403, 9413, 9419, 9421, 9431, 9433, 9437, 9439, 9461, 9463, 9467, 9473, 9479, 9491, 9497, 9511, 9521, 9533,
|
||||
9539, 9547, 9551, 9587, 9601, 9613, 9619, 9623, 9629, 9631, 9643, 9649, 9661, 9677, 9679, 9689, 9697, 9719, 9721, 9733,
|
||||
9739, 9743, 9749, 9767, 9769, 9781, 9787, 9791, 9803, 9811, 9817, 9829, 9833, 9839, 9851, 9857, 9859, 9871, 9883, 9887,
|
||||
9901, 9907, 9923, 9929, 9931, 9941, 9949, 9967, 9973, 10007, 10009, 10037, 10039, 10061, 10067, 10069, 10079, 10091, 10093, 10099,
|
||||
10103, 10111, 10133, 10139, 10141, 10151, 10159, 10163, 10169, 10177, 10181, 10193, 10211, 10223, 10243, 10247, 10253, 10259, 10267, 10271,
|
||||
10273, 10289, 10301, 10303, 10313, 10321, 10331, 10333, 10337, 10343, 10357, 10369, 10391, 10399, 10427, 10429, 10433, 10453, 10457, 10459,
|
||||
10463, 10477, 10487, 10499, 10501, 10513, 10529, 10531, 10559, 10567, 10589, 10597, 10601, 10607, 10613, 10627, 10631, 10639, 10651, 10657,
|
||||
10663, 10667, 10687, 10691, 10709, 10711, 10723, 10729, 10733, 10739, 10753, 10771, 10781, 10789, 10799, 10831, 10837, 10847, 10853, 10859,
|
||||
10861, 10867, 10883, 10889, 10891, 10903, 10909, 10937, 10939, 10949, 10957, 10973, 10979, 10987, 10993, 11003, 11027, 11047, 11057, 11059,
|
||||
11069, 11071, 11083, 11087, 11093, 11113, 11117, 11119, 11131, 11149, 11159, 11161, 11171, 11173, 11177, 11197, 11213, 11239, 11243, 11251,
|
||||
11257, 11261, 11273, 11279, 11287, 11299, 11311, 11317, 11321, 11329, 11351, 11353, 11369, 11383, 11393, 11399, 11411, 11423, 11437, 11443,
|
||||
11447, 11467, 11471, 11483, 11489, 11491, 11497, 11503, 11519, 11527, 11549, 11551, 11579, 11587, 11593, 11597, 11617, 11621, 11633, 11657,
|
||||
11677, 11681, 11689, 11699, 11701, 11717, 11719, 11731, 11743, 11777, 11779, 11783, 11789, 11801, 11807, 11813, 11821, 11827, 11831, 11833,
|
||||
11839, 11863, 11867, 11887, 11897, 11903, 11909, 11923, 11927, 11933, 11939, 11941, 11953, 11959, 11969, 11971, 11981, 11987, 12007, 12011,
|
||||
12037, 12041, 12043, 12049, 12071, 12073, 12097, 12101, 12107, 12109, 12113, 12119, 12143, 12149, 12157, 12161, 12163, 12197, 12203, 12211,
|
||||
12227, 12239, 12241, 12251, 12253, 12263, 12269, 12277, 12281, 12289, 12301, 12323, 12329, 12343, 12347, 12373, 12377, 12379, 12391, 12401,
|
||||
12409, 12413, 12421, 12433, 12437, 12451, 12457, 12473, 12479, 12487, 12491, 12497, 12503, 12511, 12517, 12527, 12539, 12541, 12547, 12553,
|
||||
12569, 12577, 12583, 12589, 12601, 12611, 12613, 12619, 12637, 12641, 12647, 12653, 12659, 12671, 12689, 12697, 12703, 12713, 12721, 12739,
|
||||
12743, 12757, 12763, 12781, 12791, 12799, 12809, 12821, 12823, 12829, 12841, 12853, 12889, 12893, 12899, 12907, 12911, 12917, 12919, 12923,
|
||||
12941, 12953, 12959, 12967, 12973, 12979, 12983, 13001, 13003, 13007, 13009, 13033, 13037, 13043, 13049, 13063, 13093, 13099, 13103, 13109,
|
||||
13121, 13127, 13147, 13151, 13159, 13163, 13171, 13177, 13183, 13187, 13217, 13219, 13229, 13241, 13249, 13259, 13267, 13291, 13297, 13309,
|
||||
13313, 13327, 13331, 13337, 13339, 13367, 13381, 13397, 13399, 13411, 13417, 13421, 13441, 13451, 13457, 13463, 13469, 13477, 13487, 13499,
|
||||
13513, 13523, 13537, 13553, 13567, 13577, 13591, 13597, 13613, 13619, 13627, 13633, 13649, 13669, 13679, 13681, 13687, 13691, 13693, 13697,
|
||||
13709, 13711, 13721, 13723, 13729, 13751, 13757, 13759, 13763, 13781, 13789, 13799, 13807, 13829, 13831, 13841, 13859, 13873, 13877, 13879,
|
||||
13883, 13901, 13903, 13907, 13913, 13921, 13931, 13933, 13963, 13967, 13997, 13999, 14009, 14011, 14029, 14033, 14051, 14057, 14071, 14081,
|
||||
14083, 14087, 14107, 14143, 14149, 14153, 14159, 14173, 14177, 14197, 14207, 14221, 14243, 14249, 14251, 14281, 14293, 14303, 14321, 14323,
|
||||
14327, 14341, 14347, 14369, 14387, 14389, 14401, 14407, 14411, 14419, 14423, 14431, 14437, 14447, 14449, 14461, 14479, 14489, 14503, 14519,
|
||||
14533, 14537, 14543, 14549, 14551, 14557, 14561, 14563, 14591, 14593, 14621, 14627, 14629, 14633, 14639, 14653, 14657, 14669, 14683, 14699,
|
||||
14713, 14717, 14723, 14731, 14737, 14741, 14747, 14753, 14759, 14767, 14771, 14779, 14783, 14797, 14813, 14821, 14827, 14831, 14843, 14851,
|
||||
14867, 14869, 14879, 14887, 14891, 14897, 14923, 14929, 14939, 14947, 14951, 14957, 14969, 14983, 15013, 15017, 15031, 15053, 15061, 15073,
|
||||
15077, 15083, 15091, 15101, 15107, 15121, 15131, 15137, 15139, 15149, 15161, 15173, 15187, 15193, 15199, 15217, 15227, 15233, 15241, 15259,
|
||||
15263, 15269, 15271, 15277, 15287, 15289, 15299, 15307, 15313, 15319, 15329, 15331, 15349, 15359, 15361, 15373, 15377, 15383, 15391, 15401,
|
||||
15413, 15427, 15439, 15443, 15451, 15461, 15467, 15473, 15493, 15497, 15511, 15527, 15541, 15551, 15559, 15569, 15581, 15583, 15601, 15607,
|
||||
15619, 15629, 15641, 15643, 15647, 15649, 15661, 15667, 15671, 15679, 15683, 15727, 15731, 15733, 15737, 15739, 15749, 15761, 15767, 15773,
|
||||
15787, 15791, 15797, 15803, 15809, 15817, 15823, 15859, 15877, 15881, 15887, 15889, 15901, 15907, 15913, 15919, 15923, 15937, 15959, 15971,
|
||||
15973, 15991, 16001, 16007, 16033, 16057, 16061, 16063, 16067, 16069, 16073, 16087, 16091, 16097, 16103, 16111, 16127, 16139, 16141, 16183,
|
||||
16187, 16189, 16193, 16217, 16223, 16229, 16231, 16249, 16253, 16267, 16273, 16301, 16319, 16333, 16339, 16349, 16361, 16363, 16369, 16381,
|
||||
16411, 16417, 16421, 16427, 16433, 16447, 16451, 16453, 16477, 16481, 16487, 16493, 16519, 16529, 16547, 16553, 16561, 16567, 16573, 16603,
|
||||
16607, 16619, 16631, 16633, 16649, 16651, 16657, 16661, 16673, 16691, 16693, 16699, 16703, 16729, 16741, 16747, 16759, 16763, 16787, 16811,
|
||||
16823, 16829, 16831, 16843, 16871, 16879, 16883, 16889, 16901, 16903, 16921, 16927, 16931, 16937, 16943, 16963, 16979, 16981, 16987, 16993,
|
||||
17011, 17021, 17027, 17029, 17033, 17041, 17047, 17053, 17077, 17093, 17099, 17107, 17117, 17123, 17137, 17159, 17167, 17183, 17189, 17191,
|
||||
17203, 17207, 17209, 17231, 17239, 17257, 17291, 17293, 17299, 17317, 17321, 17327, 17333, 17341, 17351, 17359, 17377, 17383, 17387, 17389,
|
||||
}
|
||||
Reference in New Issue
Block a user