Repository: fentec-project/gofe Branch: master Commit: ccc7482d20ef Files: 89 Total size: 452.9 KB Directory structure: gitextract_ii1m5ucw/ ├── .circleci/ │ └── config.yml ├── .gitignore ├── AUTHORS.md ├── LICENSE ├── README.md ├── abe/ │ ├── dippe.go │ ├── dippe_test.go │ ├── doc.go │ ├── fame.go │ ├── fame_test.go │ ├── gpsw.go │ ├── gpsw_test.go │ ├── ma-abe.go │ ├── ma-abe_test.go │ ├── policy.go │ └── policy_test.go ├── data/ │ ├── doc.go │ ├── matrix.go │ ├── matrix_bn256.go │ ├── matrix_test.go │ ├── vector.go │ ├── vector_bn256.go │ └── vector_test.go ├── doc.go ├── go.mod ├── go.sum ├── innerprod/ │ ├── doc.go │ ├── fullysec/ │ │ ├── damgard.go │ │ ├── damgard_dec_multi.go │ │ ├── damgard_dec_multi_test.go │ │ ├── damgard_multi.go │ │ ├── damgard_multi_test.go │ │ ├── damgard_test.go │ │ ├── dmcfe.go │ │ ├── dmcfe_test.go │ │ ├── doc.go │ │ ├── fh_multi_ipe.go │ │ ├── fh_multi_ipe_test.go │ │ ├── fhipe.go │ │ ├── fhipe_test.go │ │ ├── lwe.go │ │ ├── lwe_test.go │ │ ├── paillier.go │ │ ├── paillier_multi.go │ │ ├── paillier_multi_test.go │ │ ├── paillier_test.go │ │ ├── part_fh_ipe.go │ │ └── part_fh_ipe_test.go │ └── simple/ │ ├── ddh.go │ ├── ddh_multi.go │ ├── ddh_multi_test.go │ ├── ddh_test.go │ ├── doc.go │ ├── lwe.go │ ├── lwe_test.go │ ├── ringlwe.go │ └── ringlwe_test.go ├── internal/ │ ├── dlog/ │ │ ├── brute_force.go │ │ ├── brute_force_test.go │ │ ├── calc.go │ │ ├── calc_test.go │ │ ├── dlog_test.go │ │ ├── doc.go │ │ ├── pollard_rho.go │ │ └── pollard_rho_test.go │ ├── errors.go │ ├── keygen/ │ │ ├── elgamal.go │ │ └── prime.go │ └── mod_exp.go ├── quadratic/ │ ├── doc.go │ ├── quad.go │ ├── quad_test.go │ ├── sgp.go │ └── sgp_test.go └── sample/ ├── doc.go ├── normal.go ├── normal_cdt.go ├── normal_cdt_test.go ├── normal_cumulative.go ├── normal_cumulative_test.go ├── normal_double.go ├── normal_double_constant.go ├── normal_double_constant_test.go ├── normal_double_test.go ├── normal_negative.go ├── normal_negative_test.go ├── normal_test.go ├── sampler.go └── uniform.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .circleci/config.yml ================================================ version: 2 jobs: build: docker: - image: circleci/golang:1.14 working_directory: /go/src/github.com/fentec-project/gofe steps: - checkout - run: name: Get dependencies command: go get -t -v ./... - run: name: Run tests command: go test -v ./... ================================================ FILE: .gitignore ================================================ # IDE files .idea .vscode # Vendor directory vendor/*/ # Other *.swp *.swo .DS_Store # Binary fentec ================================================ FILE: AUTHORS.md ================================================ # Authors A list of code contributors to GoFE library for functional encryption. ## Maintainer The team from [XLAB d.o.o](https://www.xlab.si/) ## Original authors * Manca Bizjak * Jan Hartman * Tilen Marc * Miha Stopar ================================================ FILE: LICENSE ================================================ 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 APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} 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. ================================================ FILE: README.md ================================================ # GoFE - Functional Encryption library [![Build Status](https://circleci.com/gh/fentec-project/gofe.svg?style=svg)](https://circleci.com/gh/fentec-project/gofe) [![GoDoc](https://godoc.org/github.com/fentec-project/gofe?status.svg)](https://godoc.org/github.com/fentec-project/gofe) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/3ba91378a50b4446852200cc6391b4e2)](https://www.codacy.com/gh/fentec-project/gofe/dashboard?utm_source=github.com&utm_medium=referral&utm_content=fentec-project/gofe&utm_campaign=Badge_Grade)

GoFE is a cryptographic library offering different state-of-the-art implementations of functional encryption schemes, specifically FE schemes for _linear_ (e.g. _inner products_) and _quadratic polynomials_. To quickly get familiar with FE, read a short and very high-level introduction on our [Introductory Wiki page](../../wiki/Introduction-to-FE). A more detailed introduction with lots of interactive diagrams can be found on [this blog](https://alicebobstory.com/fe). - [Installing GoFE](#installing-gofe) - [Using GoFE in your project](#using-gofe-in-your-project) * [Select the FE scheme](#select-the-fe-scheme) * [Configure selected scheme](#configure-selected-scheme) * [Prepare input data](#prepare-input-data) * [Use the scheme (examples)](#use-the-scheme-(examples)) - [Related work](#related-work) * [Other implementations](#other-implementations) * [Example projects](#example-projects) ### Before using the library Please note that the library is a work in progress and has not yet reached a stable release. Code organization and APIs are **not stable**. You can expect them to change at any point. The purpose of GoFE is to support research and proof-of-concept implementations. It **should not be used in production**. ## Installing GoFE First, download and build the library by running either `go install github.com/fentec-project/gofe/...` or `go get -u -t github.com/fentec-project/gofe/...` from the terminal (note that this also downloads and builds all the dependencies of the library). Please note that from Go version 1.18 on, `go get` will [no longer build packages](https://golang.org/doc/go-get-install-deprecation), and `go install` should be used instead. To make sure the library works as expected, navigate to your `$GOPATH/pkg/mod/github.com/fentec-project/gofe` directory and run `go test -v ./...` . If you are still using Go version below 1.16 or have `GO111MODULE=off` set, navigate to `$GOPATH/src/github.com/fentec-project/gofe` instead. ## Using GoFE in your project After you have successfully built the library, you can use it in your project. Instructions below provide a brief introduction to the most important parts of the library, and guide you through a sequence of steps that will quickly get your FE example up and running. ### Select the FE scheme You can choose from the following set of schemes: #### Inner product schemes You will need to import packages from `ìnnerprod` directory. We organized implementations in two categories based on their security assumptions: * Schemes with **selective security under chosen-plaintext attacks** (s-IND-CPA security): * Scheme by _Abdalla, Bourse, De Caro, Pointcheval_ ([paper](https://eprint.iacr.org/2015/017.pdf)). The scheme can be instantiated from DDH (`simple.DDH`), LWE (`simple.LWE`) primitives. * Ring-LWE scheme based on _Bermudo Mera, Karmakar, Marc, and Soleimanian_ ([paper](https://eprint.iacr.org/2021/046)), see `simple.RingLWE`. * Multi-input scheme based on paper by _Abdalla, Catalano, Fiore, Gay, Ursu_ ([paper](https://eprint.iacr.org/2017/972.pdf)) and instantiated from the scheme in the first point (`simple.DDHMulti`). * Schemes with stronger **adaptive security under chosen-plaintext attacks** (IND-CPA security) or **simulation based security** (SIM-Security for IPE): * Scheme based on paper by _Agrawal, Libert and Stehlé_ ([paper](https://eprint.iacr.org/2015/608.pdf)). It can be instantiated from Damgard DDH (`fullysec.Damgard` - similar to `simple.DDH`, but uses one more group element to achieve full security, similar to how Damgård's encryption scheme is obtained from ElGamal scheme ([paper](https://link.springer.com/chapter/10.1007/3-540-46766-1_36)), LWE (`fullysec.LWE`) and Paillier (`fullysec.Paillier`) primitives. * Multi-input scheme based on paper by _Abdalla, Catalano, Fiore, Gay, Ursu_ ([paper](https://eprint.iacr.org/2017/972.pdf)) and instantiated from the scheme in the first point (`fullysec.DamgardMulti`). * Decentralized scheme based on paper by _Chotard, Dufour Sans, Gay, Phan and Pointcheval_ ([paper](https://eprint.iacr.org/2017/989.pdf)). This scheme does not require a trusted party to generate keys. It is built on pairings (`fullysec.DMCFEClient`). * Decentralized scheme based on paper by _Abdalla, Benhamouda, Kohlweiss, Waldner_ ([paper](https://eprint.iacr.org/2019/020.pdf)). Similarly as above this scheme this scheme does not require a trusted party to generate keys and is based on a general procedure for decentralization of an inner product scheme, in particular the decentralization of a Damgard DDH scheme (`fullysec.DamgardDecMultiClient`). * Function hiding multi-input scheme based on paper by _Datta, Okamoto, Tomida_ ([paper](https://eprint.iacr.org/2018/061.pdf)). This scheme allows clients to encrypt vectors and derive functional key that allows a decrytor to decrypt an inner product without revealing the ciphertext or the function (`fullysec.FHMultiIPE`). * Function hiding inner product scheme by _Kim, Lewi, Mandal, Montgomery, Roy, Wu_ ([paper](https://eprint.iacr.org/2016/440.pdf)). The scheme allows the decryptor to decrypt the inner product of x and y without reveling (ciphertext) x or (function) y (`fullysec.fhipe`). * Partially function hiding inner product scheme by _Romain Gay_ ([paper](https://eprint.iacr.org/2020/093.pdf)). This scheme is a public key inner product scheme that decrypt the inner product of x and y without reveling (ciphertext) x or (function) y. This is achieved by limiting the space of vectors that can be encrypted with a public key (`fullysec.partFHIPE`). #### Quadratic polynomial schemes There are two implemented FE schemes for **quadratic multi-variate polynomials**: * First is an efficient symmetric FE scheme by _Dufour Sans, Gay_ and _Pointcheval_ ([paper](https://eprint.iacr.org/2018/206.pdf)) which is based on bilinear pairings, and offers adaptive security under chosen-plaintext attacks (IND-CPA security). You will need `SGP` scheme from package `quadratic`. * Second is an efficient pubic key FE by _Romain Gay_ ([paper](https://eprint.iacr.org/2020/093.pdf)) that is based on the underlying partially function hiding inner product scheme and offers semi-adaptive simulation based security. You will need `quad` scheme from package `quadratic`. #### Schemes with the attribute based encryption (ABE) Schemes are organized under package `abe`. It contains four ABE schemes: * A ciphertext policy (CP) ABE scheme named FAME by _Agrawal, Chase_ ([paper](https://eprint.iacr.org/2017/807.pdf)) allowing encrypting a message based on a boolean expression defining a policy which attributes are needed for the decryption. It is implemented in `abe.fame`. * A key policy (KP) ABE scheme by _Goyal, Pandey, Sahai, Waters_ ([paper](https://eprint.iacr.org/2006/309.pdf)) allowing a distribution of keys following a boolean expression defining a policy which attributes are needed for the decryption. It is implemented in `abe.gpsw`. * A decentralized inner product predicate scheme by _Michalevsky, Joye_ ([paper](https://eprint.iacr.org/2018/753.pdf)) allowing encryption with policy described as a vector, and a decentralized distribution of keys based on users' vectors so that only users with vectors orthogonal to the encryption vector posses a key that can decrypt the ciphertext. It is implemented in `abe.dippe`. * A multi-authority (MA) ciphertext policy (CP) ABE scheme by _Lewko, Waters_ ([paper](https://eprint.iacr.org/2010/351.pdf)) based on a boolean expression defining a policy which attributes are needed for decryption. This scheme is decentralized - the attributes can be spread across multiple different authorites. It is implemented in `abe.ma-abe`. ### Configure selected scheme All GoFE schemes are implemented as Go structs with (at least logically) similar APIs. So the first thing we need to do is to create a scheme instance by instantiating the appropriate struct. For this step, we need to pass in some configuration, e.g. values of parameters for the selected scheme. Let's say we selected a `simple.DDH` scheme. We create a new scheme instance with: ````go scheme, _ := simple.NewDDH(5, 1024, big.NewInt(1000)) ```` In the line above, the first argument is length of input vectors **x** and **y**, the second argument is bit length of prime modulus _p_ (because this particular scheme operates in the ℤp group), and the last argument represents the upper bound for elements of input vectors. However, configuration parameters for different FE schemes vary quite a bit. Please refer to [library documentation](https://godoc.org/github.com/fentec-project/gofe) regarding the meaning of parameters for specific schemes. For now, examples and reasonable defaults can be found in the test code. After you successfully created a FE scheme instance, you can call its methods for: * generation of (secret and public) master keys, * derivation of functional encryption key, * encryption, and * decryption. ### Prepare input data #### Vectors and matrices All GoFE chemes rely on vectors (or matrices) of big integer (`*big.Int`) components. GoFE schemes use the library's own `Vector` and `Matrix` types. They are implemented in the `data` package. A `Vector` is basically a wrapper around `[]*big.Int` slice, while a `Matrix` wraps a slice of `Vector`s. In general, you only have to worry about providing input data (usually vectors **x** and **y**). If you already have your slice of `*big.Int`s defined, you can create a `Vector` by calling `data.NewVector` function with your slice as argument, for example: ````go // Let's say you already have your data defined in a slice of *big.Ints x := []*big.Int{big.NewInt(0), big.NewInt(1), big.NewInt(2)} xVec := data.NewVector(x) ```` Similarly, for matrices, you will first have to construct your slice of `Vector`s, and pass it to `data.NewMatrix` function: ````go vecs := make([]data.Vector, 3) // a slice of 3 vectors // fill vecs vecs[0] := []*big.Int{big.NewInt(0), big.NewInt(1), big.NewInt(2)} vecs[1] := []*big.Int{big.NewInt(2), big.NewInt(1), big.NewInt(0)} vecs[2] := []*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1)} xMat := data.NewMatrix(vecs) ```` #### Random data To generate random `*big.Int` values from different probability distributions, you can use one of our several implementations of random samplers. The samplers are provided in the `sample` package and all implement `sample.Sampler` interface. You can quickly construct random vectors and matrices by: 1. Configuring the sampler of your choice, for example: ````go s := sample.NewUniform(big.NewInt(100)) // will sample uniformly from [0,100) ```` 2. Providing it as an argument to`data.NewRandomVector` or `data.NewRandomMatrix` functions. ````go x, _ := data.NewRandomVector(5, s) // creates a random vector with 5 elements X, _ := data.NewRandomMatrix(2, 3, s) // creates a random 2x3 matrix ```` ## Use the scheme To see how the schemes can be used consult one of the following. #### Tests Every implemented scheme has an implemented test to verify the correctness of the implementation (for example Paillier inner-product scheme implemented in `innerprod/fullysec/paillier.go` has a corresponding test in `innerprod/fullysec/paillier_test.go`). One can check the appropriate test file to see an example of how the chosen scheme can be used. #### Examples We give some concrete examples how to use the schemes. Please note that all the examples below omit error management. ##### Using a single input scheme The example below demonstrates how to use single input scheme instances. Although the example shows how to use the`DDH` from package `simple`, the usage is similar for all single input schemes, regardless of their security properties (s-IND-CPA or IND-CPA) and instantiation (DDH or LWE). You will see that three `DDH` structs are instantiated to simulate the real-world scenarios where each of the three entities involved in FE are on separate machines. ```go // Instantiation of a trusted entity that // will generate master keys and FE key l := 2 // length of input vectors bound := big.NewInt(10) // upper bound for input vector coordinates modulusLength := 2048 // bit length of prime modulus p trustedEnt, _ := simple.NewDDHPrecomp(l, modulusLength, bound) msk, mpk, _ := trustedEnt.GenerateMasterKeys() y := data.NewVector([]*big.Int{big.NewInt(1), big.NewInt(2)}) feKey, _ := trustedEnt.DeriveKey(msk, y) // Simulate instantiation of encryptor // Encryptor wants to hide x and should be given // master public key by the trusted entity enc := simple.NewDDHFromParams(trustedEnt.Params) x := data.NewVector([]*big.Int{big.NewInt(3), big.NewInt(4)}) cipher, _ := enc.Encrypt(x, mpk) // Simulate instantiation of decryptor that decrypts the cipher // generated by encryptor. dec := simple.NewDDHFromParams(trustedEnt.Params) // decrypt to obtain the result: inner prod of x and y // we expect xy to be 11 (e.g. <[1,2],[3,4]>) xy, _ := dec.Decrypt(cipher, feKey, y) ``` ##### Using a multi input scheme This example demonstrates how multi input FE schemes can be used. Here we assume that there are `numClients` encryptors (ei), each with their corresponding input vector xi. A trusted entity generates all the master keys needed for encryption and distributes appropriate keys to appropriate encryptor. Then, encryptor ei uses their keys to encrypt their data xi. The decryptor collects ciphers from all the encryptors. It then relies on the trusted entity to derive a decryption key based on its own set of vectors yi. With the derived key, the decryptor is able to compute the result - inner product over all vectors, as _Σ i,yi>._ ```go numClients := 2 // number of encryptors l := 3 // length of input vectors bound := big.NewInt(1000) // upper bound for input vectors // Simulate collection of input data. // X and Y represent matrices of input vectors, where X are collected // from numClients encryptors (omitted), and Y is only known by a single decryptor. // Encryptor i only knows its own input vector X[i]. sampler := sample.NewUniform(bound) X, _ := data.NewRandomMatrix(numClients, l, sampler) Y, _ := data.NewRandomMatrix(numClients, l, sampler) // Trusted entity instantiates scheme instance and generates // master keys for all the encryptors. It also derives the FE // key derivedKey for the decryptor. modulusLength := 2048 multiDDH, _ := simple.NewDDHMultiPrecomp(numClients, l, modulusLength, bound) pubKey, secKey, _ := multiDDH.GenerateMasterKeys() derivedKey, _ := multiDDH.DeriveKey(secKey, Y) // Different encryptors may reside on different machines. // We simulate this with the for loop below, where numClients // encryptors are generated. encryptors := make([]*simple.DDHMultiClient, numClients) for i := 0; i < numClients; i++ { encryptors[i] = simple.NewDDHMultiClient(multiDDH.Params) } // Each encryptor encrypts its own input vector X[i] with the // keys given to it by the trusted entity. ciphers := make([]data.Vector, numClients) for i := 0; i < numClients; i++ { cipher, _ := encryptors[i].Encrypt(X[i], pubKey[i], secKey.OtpKey[i]) ciphers[i] = cipher } // Ciphers are collected by decryptor, who then computes // inner product over vectors from all encryptors. decryptor := simple.NewDDHMultiFromParams(numClients, multiDDH.Params) prod, _ = decryptor.Decrypt(ciphers, derivedKey, Y) ``` Note that above we instantiate multiple encryptors - in reality, different encryptors will be instantiated on different machines. ##### Using quadratic polynomial scheme In the example below, we omit instantiation of different entities (encryptor and decryptor). ```go l := 2 // length of input vectors bound := big.NewInt(10) // Upper bound for coordinates of vectors x, y, and matrix F // Here we fill our vectors and the matrix F (that represents the // quadratic function) with random data from [0, bound). sampler := sample.NewUniform(bound) F, _ := data.NewRandomMatrix(l, l, sampler) x, _ := data.NewRandomVector(l, sampler) y, _ := data.NewRandomVector(l, sampler) sgp := quadratic.NewSGP(l, bound) // Create scheme instance msk, _ := sgp.GenerateMasterKey() // Create master secret key cipher, _ := sgp.Encrypt(x, y, msk) // Encrypt input vectors x, y with secret key key, _ := sgp.DeriveKey(msk, F) // Derive FE key for decryption dec, _ := sgp.Decrypt(cipher, key, F) // Decrypt the result to obtain x^T * F * y ``` ##### Using ABE schemes Let's say we selected `abe.FAME` scheme. In the example below, we omit instantiation of different entities (encryptor and decryptor). Say we want to encrypt the following message `msg` so that only those who own the attributes satisfying a boolean expression 'policy' can decrypt. ```go msg := "Attack at dawn!" policy := "((0 AND 1) OR (2 AND 3)) AND 5" gamma := []string{"0", "2", "3", "5"} // owned attributes a := abe.NewFAME() // Create the scheme instance pubKey, secKey, _ := a.GenerateMasterKeys() // Create a public key and a master secret key msp, _ := abe.BooleanToMSP(policy, false) // The MSP structure defining the policy cipher, _ := a.Encrypt(msg, msp, pubKey) // Encrypt msg with policy msp under public key pubKey keys, _ := a.GenerateAttribKeys(gamma, secKey) // Generate keys for the entity with attributes gamma dec, _ := a.Decrypt(cipher, keys, pubKey) // Decrypt the message ``` ## Related work ### Other implementations Apart from the GoFE library, there is also a C library called CiFEr that implements many of the same schemes as GoFE, and can be found [here](https://github.com/fentec-project/CiFEr). ### Example projects A few reference uses of the GoFE library are provided: * [creating a privacy preserving heatmap](https://github.com/fentec-project/FE-anonymous-heatmap), * [evaluating a machine learning function on encrypted data](https://github.com/fentec-project/neural-network-on-encrypted-data), * [privacy friendly data analysis on encrypted data](https://github.com/fentec-project/privacy-friendly-analyses). ================================================ FILE: abe/dippe.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package abe import ( "crypto/aes" cbc "crypto/cipher" "crypto/rand" "crypto/sha256" "fmt" "math/big" "strconv" "io" "github.com/fentec-project/bn256" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/sample" ) // DIPPE represents a Decentralized Inner-Product Predicate Encryption // (DIPPE) scheme introduced by Y. Michalevsky and M. Joye in: // "Decentralized Policy-Hiding Attribute-Based Encryption with Receiver Privacy" // https://eprint.iacr.org/2018/753.pdf type DIPPE struct { secLevel int G1ToA data.MatrixG1 G1ToUA data.MatrixG1 P *big.Int // order of the elliptic curve } // DIPPEPubKey represents a public key of an authority in DIPPE scheme. type DIPPEPubKey struct { G1ToWtA data.MatrixG1 GToAlphaA data.VectorGT G2ToSigma *bn256.G2 } // DIPPESecKey represents a secret key of an authority in DIPPE scheme. type DIPPESecKey struct { Sigma *big.Int W data.Matrix Alpha data.Vector } // DIPPEAuth represents an authority in DIPPE scheme type DIPPEAuth struct { ID int Sk DIPPESecKey Pk DIPPEPubKey } // DIPPECipher represents a ciphertext in DIPPE scheme type DIPPECipher struct { C0 data.VectorG1 C data.MatrixG1 CPrime *bn256.GT X data.Vector // policy vector SymEnc []byte // symmetric encryption of the message Iv []byte // initialization vector for symmetric encryption } // NewDIPPE configures a new instance of the scheme. The input parameter // defines the security assumption of the scheme, so called k-Lin assumption, // where k is the input. func NewDIPPE(secLevel int) (*DIPPE, error) { sampler := sample.NewUniform(bn256.Order) A, err := data.NewRandomMatrix(secLevel+1, secLevel, sampler) if err != nil { return nil, err } g1ToA := A.MulG1() U, err := data.NewRandomMatrix(secLevel+1, secLevel+1, sampler) if err != nil { return nil, err } UA, err := U.Mul(A) if err != nil { return nil, err } UA.Mod(bn256.Order) g1ToUA := UA.MulG1() return &DIPPE{secLevel: secLevel, G1ToA: g1ToA, G1ToUA: g1ToUA, P: bn256.Order}, nil } // NewDIPPEAuth configures a new authority that will be able to // produce decryption keys. If the scheme will have n authorities // it is assumed that each will have a different id from [0, n). func (d *DIPPE) NewDIPPEAuth(id int) (*DIPPEAuth, error) { sampler := sample.NewUniform(bn256.Order) W, err := data.NewRandomMatrix(d.secLevel+1, d.secLevel+1, sampler) if err != nil { return nil, err } alpha, err := data.NewRandomVector(d.secLevel+1, sampler) if err != nil { return nil, err } sigma, err := sampler.Sample() if err != nil { return nil, err } sk := DIPPESecKey{W: W, Alpha: alpha, Sigma: sigma} g1ToWtA, err := W.Transpose().MatMulMatG1(d.G1ToA) if err != nil { return nil, err } alphaAsMatrix := data.Matrix([]data.Vector{alpha}) g1ToAlphaA, err := alphaAsMatrix.MatMulMatG1(d.G1ToA) if err != nil { return nil, err } g2 := new(bn256.G2).ScalarBaseMult(big.NewInt(1)) gtToAlphaA := make(data.VectorGT, d.secLevel) for i := 0; i < d.secLevel; i++ { gtToAlphaA[i] = bn256.Pair(g1ToAlphaA[0][i], g2) } g2ToSigma := new(bn256.G2).ScalarMult(g2, sigma) pk := DIPPEPubKey{G1ToWtA: g1ToWtA, GToAlphaA: gtToAlphaA, G2ToSigma: g2ToSigma} return &DIPPEAuth{ID: id, Sk: sk, Pk: pk}, nil } // Encrypt takes as an input a string message msg, a vector x representing a // decryption policy and a slice of public keys of the participating authorities. // The i-th coordinate of x corresponds to i-th public key of the authority with // id i. It returns an encryption of msg. In case of a failed procedure an // error is returned. func (d *DIPPE) Encrypt(msg string, x data.Vector, pubKeys []*DIPPEPubKey) (*DIPPECipher, error) { // msg is encrypted using CBC, with a random key that is encapsulated // with DIPPE _, keyGt, err := bn256.RandomGT(rand.Reader) if err != nil { return nil, err } keyCBC := sha256.Sum256([]byte(keyGt.String())) a, err := aes.NewCipher(keyCBC[:]) if err != nil { return nil, err } iv := make([]byte, a.BlockSize()) _, err = io.ReadFull(rand.Reader, iv) if err != nil { return nil, err } encrypterCBC := cbc.NewCBCEncrypter(a, iv) msgByte := []byte(msg) // message is padded according to pkcs7 standard padLen := a.BlockSize() - (len(msgByte) % a.BlockSize()) msgPad := make([]byte, len(msgByte)+padLen) copy(msgPad, msgByte) for i := len(msgByte); i < len(msgPad); i++ { msgPad[i] = byte(padLen) } symEnc := make([]byte, len(msgPad)) encrypterCBC.CryptBlocks(symEnc, msgPad) // encapsulate the key with DIPPE sampler := sample.NewUniform(bn256.Order) s, err := data.NewRandomVector(d.secLevel, sampler) if err != nil { return nil, err } c0 := d.G1ToA.MulVector(s) if err != nil { return nil, err } c := make(data.MatrixG1, len(x)) for i := range x { g1ToXiUA := d.G1ToUA.MulScalar(x[i]) g1ToXiUplusWtiA := g1ToXiUA.Add(pubKeys[i].G1ToWtA) c[i] = g1ToXiUplusWtiA.MulVector(s) } cPrime := new(bn256.GT).ScalarBaseMult(big.NewInt(0)) for _, e := range pubKeys { tmp := e.GToAlphaA.Dot(s) cPrime.Add(tmp, cPrime) } cPrime.Add(keyGt, cPrime) return &DIPPECipher{C0: c0, C: c, CPrime: cPrime, X: x.Copy(), SymEnc: symEnc, Iv: iv}, nil } // DeriveKeyShare allows an authority to give a partial decryption key. Collecting all // such partial keys allows a user to decrypt the message. The input vector v contains // an information about the user that will allow him to decrypt iff the inner product // v times x = 0 for the policy x. GID is a global identifier of the user and a slice of // public keys of the authorities should be given. func (a *DIPPEAuth) DeriveKeyShare(v data.Vector, pubKeys []*DIPPEPubKey, gid string) (data.VectorG2, error) { g2ToMu := make(data.VectorG2, a.Sk.W.Rows()) for i := 0; i < a.Sk.W.Rows(); i++ { g2ToMu[i] = new(bn256.G2).ScalarBaseMult(big.NewInt(0)) } g2ToAlpha := a.Sk.Alpha.MulG2() var err error for j := 0; j < len(pubKeys); j++ { if j == a.ID { continue } yToSigma := new(bn256.G2).ScalarMult(pubKeys[j].G2ToSigma, a.Sk.Sigma) for i := 0; i < a.Sk.W.Rows(); i++ { hashed, err := bn256.HashG2(strconv.Itoa(i) + yToSigma.String() + gid + v.String()) if err != nil { return nil, err } if j > a.ID { hashed.Neg(hashed) } g2ToMu[i] = g2ToMu[i].Add(hashed, g2ToMu[i]) } } g2ToH := make(data.VectorG2, a.Sk.W.Rows()) for j := range g2ToH { g2ToH[j], err = bn256.HashG2(strconv.Itoa(j) + gid + v.String()) if err != nil { return nil, err } } g2ToWH, err := a.Sk.W.MatMulVecG2(g2ToH) if err != nil { return nil, err } g2ToViWH := g2ToWH.MulScalar(v[a.ID]).Neg() return g2ToAlpha.Add(g2ToViWH).Add(g2ToMu), nil } // Decrypt accepts the ciphertext, a slice of keys obtained from the authorities, // a vector v representing the users decryption allowance, and a global identifier. // If the provided keys are correct and the inner product v times x = 0 for the policy // x, the message is decrypted, otherwise an error is returned. func (d *DIPPE) Decrypt(cipher *DIPPECipher, keys []data.VectorG2, v data.Vector, gid string) (string, error) { // check if the decryption is possible prod, err := v.Dot(cipher.X) if err != nil { return "", err } if prod.Sign() != 0 { return "", fmt.Errorf("insufficient keys") } // use DIPPE decryption procedure to get a CBC key // needed for the decryption of the message gTToAlphaAS := new(bn256.GT).ScalarBaseMult(big.NewInt(0)) ones := data.NewConstantMatrix(1, len(keys), big.NewInt(1)) sum, err := ones.MatMulMatG2(data.MatrixG2(keys)) if err != nil { return "", err } for i, e := range cipher.C0 { tmpGT := bn256.Pair(e, sum[0][i]) gTToAlphaAS.Add(gTToAlphaAS, tmpGT) } vMat := make(data.Matrix, 1) vMat[0] = v cSum, err := vMat.MatMulMatG1(cipher.C) if err != nil { return "", err } for j := range cSum[0] { hashed, err := bn256.HashG2(strconv.Itoa(j) + gid + v.String()) if err != nil { return "", err } tmpGT := bn256.Pair(cSum[0][j], hashed) gTToAlphaAS.Add(gTToAlphaAS, tmpGT) } gTToAlphaAS.Neg(gTToAlphaAS) keyGt := new(bn256.GT).Add(cipher.CPrime, gTToAlphaAS) keyCBC := sha256.Sum256([]byte(keyGt.String())) a, err := aes.NewCipher(keyCBC[:]) if err != nil { return "", err } msgPad := make([]byte, len(cipher.SymEnc)) decrypter := cbc.NewCBCDecrypter(a, cipher.Iv) decrypter.CryptBlocks(msgPad, cipher.SymEnc) // unpad the message padLen := int(msgPad[len(msgPad)-1]) if (len(msgPad) - padLen) < 0 { return "", fmt.Errorf("failed to decrypt") } msgByte := msgPad[0:(len(msgPad) - padLen)] return string(msgByte), nil } // ExactThresholdPolicyVecInit is used for the transformation of the DIPPE // scheme into an ABE scheme with an exact threshold. In particular given a // slice of attributes, a threshold value and the number of all possible // attributes it creates a policy vector that can be used for the DIPPE encryption. // The user will be able to decrypt only if he posses exactly the threshold // value of the attributes. func (d DIPPE) ExactThresholdPolicyVecInit(attrib []int, threshold int, numAttrib int) (data.Vector, error) { policyVec := data.NewConstantVector(numAttrib+1, big.NewInt(0)) one := big.NewInt(1) for _, e := range attrib { if e > numAttrib { return nil, fmt.Errorf("attributes out of range") } policyVec[e].Set(one) } policyVec[numAttrib].Set(big.NewInt(int64(-threshold))) return policyVec, nil } // ConjunctionPolicyVecInit is used for the transformation of the DIPPE // scheme into an ABE scheme with conjugation policy. In particular given a // slice of attributes and the number of all possible attributes it creates // a policy vector that can be used for the DIPPE encryption. The user will // be able to decrypt only if he posses all the demanded attributes. func (d DIPPE) ConjunctionPolicyVecInit(attrib []int, numAttrib int) (data.Vector, error) { policyVec := data.NewConstantVector(numAttrib+1, big.NewInt(0)) sampler := sample.NewUniform(bn256.Order) last := big.NewInt(0) for _, e := range attrib { if e > numAttrib { return nil, fmt.Errorf("attributes out of range") } tmp, err := sampler.Sample() if err != nil { return nil, err } policyVec[e].Set(tmp) last.Sub(last, tmp) } policyVec[numAttrib].Set(last) return policyVec, nil } // AttributeVecInit given the attributes and the number of all possible // attributes creates a vector describing the users allowance. The function is // needed in the the transformation of the DIPPE scheme into an ABE scheme // with threshold or conjugation. func (d DIPPE) AttributeVecInit(attrib []int, numAttrib int) (data.Vector, error) { attribVec := data.NewConstantVector(numAttrib+1, big.NewInt(0)) one := big.NewInt(1) for _, e := range attrib { if e > numAttrib { return nil, fmt.Errorf("attributes out of range") } attribVec[e].Set(one) } attribVec[numAttrib].Set(one) return attribVec, nil } ================================================ FILE: abe/dippe_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package abe_test import ( "testing" "math/big" "github.com/fentec-project/gofe/abe" "github.com/fentec-project/gofe/data" "github.com/stretchr/testify/assert" ) func TestDIPPE(t *testing.T) { // create a new DIPPE struct, choosing the security parameter d, err := abe.NewDIPPE(3) if err != nil { t.Fatalf("Failed to generate a new scheme: %v", err) } vecLen := 5 // create authorities and their public keys auth := make([]*abe.DIPPEAuth, vecLen) pubKeys := make([]*abe.DIPPEPubKey, vecLen) for i := range auth { auth[i], err = d.NewDIPPEAuth(i) if err != nil { t.Fatalf("Failed to generate a new authority: %v", err) } pubKeys[i] = &auth[i].Pk } msg := "some message" // choose a policy vector policyVec := data.Vector([]*big.Int{big.NewInt(1), big.NewInt(-1), big.NewInt(1), big.NewInt(0), big.NewInt(0)}) // encrypt the message with the chosen policy give by a policy vector, cipher, err := d.Encrypt(msg, policyVec, pubKeys) if err != nil { t.Fatalf("Failed to encrypt: %v", err) } // choose a unique user's GID userGID := "someGID" // choose user's vector, decryption is possible if and only if // the users's and policy vector are orthogonal userVec := data.Vector([]*big.Int{big.NewInt(0), big.NewInt(1), big.NewInt(1), big.NewInt(-3), big.NewInt(4)}) // authorities generate decryption keys for the user userKeys := make([]data.VectorG2, vecLen) for i := range auth { userKeys[i], err = auth[i].DeriveKeyShare(userVec, pubKeys, userGID) if err != nil { t.Fatalf("Failed to generate a user key: %v", err) } } // user decrypts using collected keys dec, err := d.Decrypt(cipher, userKeys, userVec, userGID) if err != nil { t.Fatalf("Failed to decrypt: %v", err) } assert.Equal(t, msg, dec) } func TestDIPPE_ABE_threshold(t *testing.T) { // this test transforms DIPPE scheme into an ABE scheme // with the exact threshold policy; in threshold policy each user has // attributes but only the users that have exactly the // threshold value of specified attributes can decrypt // create a new DIPPE struct, choosing the security parameter d, err := abe.NewDIPPE(3) if err != nil { t.Fatalf("Failed to generate a new scheme: %v", err) } // specify the number of all attributes numAttrib := 10 // create authorities and their public keys auth := make([]*abe.DIPPEAuth, numAttrib+1) pubKeys := make([]*abe.DIPPEPubKey, numAttrib+1) for i := range auth { auth[i], err = d.NewDIPPEAuth(i) if err != nil { t.Fatalf("Failed to generate a new authority: %v", err) } pubKeys[i] = &auth[i].Pk } msg := "some important message" // choose attributes needed for the exact threshold policy and the // threshold value thresholdAttrib := []int{0, 2, 5, 8, 9} exactThreshold := 3 // generate the exact threshold vector (this conversion allows the // DIPPE scheme to be used as an ABE threshold scheme) thresholdPolicyVec, err := d.ExactThresholdPolicyVecInit(thresholdAttrib, exactThreshold, numAttrib) if err != nil { t.Fatalf("Failed to generate threshold vector: %v", err) } // encrypt the message with the chosen threshold policy thresholdCipher, err := d.Encrypt(msg, thresholdPolicyVec, pubKeys) if err != nil { t.Fatalf("Failed to encrypt: %v", err) } // choose a unique user's GID thresholdUserGID := "thresholdGID" // choose the attributes possessed by the users thresholdUserAttrib := []int{0, 1, 5, 7, 9} // generate user's vector thresholdUserVec, err := d.AttributeVecInit(thresholdUserAttrib, numAttrib) if err != nil { t.Fatalf("Failed to generate attributes vector: %v", err) } // authorities generate decryption keys for the user thresholdUserKeys := make([]data.VectorG2, len(auth)) for i := range auth { thresholdUserKeys[i], err = auth[i].DeriveKeyShare(thresholdUserVec, pubKeys, thresholdUserGID) if err != nil { t.Fatalf("Failed to generate a user key: %v", err) } } // user decrypts using collected keys dec, err := d.Decrypt(thresholdCipher, thresholdUserKeys, thresholdUserVec, thresholdUserGID) if err != nil { t.Fatalf("Failed to decrypt: %v", err) } assert.Equal(t, msg, dec) } func TestDIPPE_ABE_conjugation(t *testing.T) { // this test transforms DIPPE scheme into an ABE scheme // with the conjugation policy; in the conjugation policy // the user must have all the specified attributes to be // able to decrypt // create a new DIPPE struct, choosing the security parameter d, err := abe.NewDIPPE(3) if err != nil { t.Fatalf("Failed to generate a new scheme: %v", err) } // specify the number of all attributes numAttrib := 10 // create authorities and their public keys auth := make([]*abe.DIPPEAuth, numAttrib+1) pubKeys := make([]*abe.DIPPEPubKey, numAttrib+1) for i := range auth { auth[i], err = d.NewDIPPEAuth(i) if err != nil { t.Fatalf("Failed to generate a new authority: %v", err) } pubKeys[i] = &auth[i].Pk } msg := "some important message" // choose attributes needed for the conjunction policy conjunctAttrib := []int{1, 2, 3, 7} // generate the conjunction vector (this conversion allows the // DIPPE scheme to be used as an ABE conjunction scheme) conjunctPolicyVec, err := d.ConjunctionPolicyVecInit(conjunctAttrib, numAttrib) if err != nil { t.Fatalf("Failed to generate conjucnction vector: %v", err) } // encrypt the message with the chosen conjunction policy conjunctCipher, err := d.Encrypt(msg, conjunctPolicyVec, pubKeys) if err != nil { t.Fatalf("Failed to encrypt: %v", err) } // choose a unique user's GID conjunctUserGID := "conjunctionGID" // choose the attributes possessed by the user conjunctUserAttrib := []int{0, 1, 2, 3, 7, 9} // generate user's vector conjunctUserVec, err := d.AttributeVecInit(conjunctUserAttrib, numAttrib) if err != nil { t.Fatalf("Failed to generate attributes vector: %v", err) } // authorities generate decryption keys for the user conjunctUserKeys := make([]data.VectorG2, len(auth)) for i := range auth { conjunctUserKeys[i], err = auth[i].DeriveKeyShare(conjunctUserVec, pubKeys, conjunctUserGID) if err != nil { t.Fatalf("Failed to generate a user key: %v", err) } } // user decrypts using collected keys dec, err := d.Decrypt(conjunctCipher, conjunctUserKeys, conjunctUserVec, conjunctUserGID) if err != nil { t.Fatalf("Failed to decrypt: %v", err) } assert.Equal(t, msg, dec) } ================================================ FILE: abe/doc.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ // Package abe includes schemes allowing attribute based encryption. package abe ================================================ FILE: abe/fame.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package abe import ( "math/big" "fmt" "strconv" "crypto/aes" cbc "crypto/cipher" "crypto/rand" "crypto/sha256" "io" "github.com/fentec-project/bn256" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/sample" ) // This is a ciphertext policy (CP) attribute based (ABE) scheme based // on Shashank Agrawal, Melissa Chase: // "FAME: Fast Attribute-based Message Encryption" // // This scheme enables encryption based on a boolean expression // determining which attributes are needed for an entity to be able // to decrypt. Moreover, secret keys are generated, where each key // is connected to some attribute, such that only a set of keys whose // attributes are sufficient can decrypt the massage. // This scheme is a PUBLIC-KEY scheme - no master secret key is needed // to encrypt the messages. // // FAME represents a FAME scheme. type FAME struct { P *big.Int // order of the elliptic curve } // NewFAME configures a new instance of the scheme. func NewFAME() *FAME { return &FAME{P: bn256.Order} } // FAMESecKey represents a master secret key of a FAME scheme. type FAMESecKey struct { PartInt [4]*big.Int PartG1 [3]*bn256.G1 } // FAMEPubKey represents a public key of a FAME scheme. type FAMEPubKey struct { PartG2 [2]*bn256.G2 PartGT [2]*bn256.GT } // GenerateMasterKeys generates a new set of public keys, needed // for encrypting data, and master secret keys needed for generating // keys for decrypting. func (a *FAME) GenerateMasterKeys() (*FAMEPubKey, *FAMESecKey, error) { sampler := sample.NewUniformRange(big.NewInt(1), a.P) val, err := data.NewRandomVector(7, sampler) if err != nil { return nil, nil, err } partInt := [4]*big.Int{val[0], val[1], val[2], val[3]} partG1 := [3]*bn256.G1{new(bn256.G1).ScalarBaseMult(val[4]), new(bn256.G1).ScalarBaseMult(val[5]), new(bn256.G1).ScalarBaseMult(val[6])} partG2 := [2]*bn256.G2{new(bn256.G2).ScalarBaseMult(val[0]), new(bn256.G2).ScalarBaseMult(val[1])} tmp1 := new(big.Int).Mod(new(big.Int).Add(new(big.Int).Mul(val[0], val[4]), val[6]), a.P) tmp2 := new(big.Int).Mod(new(big.Int).Add(new(big.Int).Mul(val[1], val[5]), val[6]), a.P) partGT := [2]*bn256.GT{new(bn256.GT).ScalarBaseMult(tmp1), new(bn256.GT).ScalarBaseMult(tmp2)} return &FAMEPubKey{PartG2: partG2, PartGT: partGT}, &FAMESecKey{PartInt: partInt, PartG1: partG1}, nil } // FAMECipher represents a ciphertext of a FAME scheme. type FAMECipher struct { Ct0 [3]*bn256.G2 Ct [][3]*bn256.G1 CtPrime *bn256.GT Msp *MSP SymEnc []byte // symmetric encryption of the message Iv []byte // initialization vector for symmetric encryption } // Encrypt takes as an input a message msg represented as an element of an elliptic // curve, a MSP struct representing the decryption policy, and a public key pk. It // returns an encryption of the message. In case of a failed procedure an error // is returned. Note that safety of the encryption is only proved if the mapping // msp.RowToAttrib from the rows of msp.Mat to attributes is injective. func (a *FAME) Encrypt(msg string, msp *MSP, pk *FAMEPubKey) (*FAMECipher, error) { if len(msp.Mat) == 0 || len(msp.Mat[0]) == 0 { return nil, fmt.Errorf("empty msp matrix") } attrib := make(map[string]bool) for _, i := range msp.RowToAttrib { if attrib[i] { return nil, fmt.Errorf("some attributes correspond to" + "multiple rows of the MSP struct, the scheme is not secure") } attrib[i] = true } // msg is encrypted using CBC, with a random key that is encapsulated // with FAME _, keyGt, err := bn256.RandomGT(rand.Reader) if err != nil { return nil, err } keyCBC := sha256.Sum256([]byte(keyGt.String())) c, err := aes.NewCipher(keyCBC[:]) if err != nil { return nil, err } iv := make([]byte, c.BlockSize()) _, err = io.ReadFull(rand.Reader, iv) if err != nil { return nil, err } encrypterCBC := cbc.NewCBCEncrypter(c, iv) msgByte := []byte(msg) // message is padded according to pkcs7 standard padLen := c.BlockSize() - (len(msgByte) % c.BlockSize()) msgPad := make([]byte, len(msgByte)+padLen) copy(msgPad, msgByte) for i := len(msgByte); i < len(msgPad); i++ { msgPad[i] = byte(padLen) } symEnc := make([]byte, len(msgPad)) encrypterCBC.CryptBlocks(symEnc, msgPad) // encapsulate the key with FAME sampler := sample.NewUniform(a.P) s, err := data.NewRandomVector(2, sampler) if err != nil { return nil, err } ct0 := [3]*bn256.G2{new(bn256.G2).ScalarMult(pk.PartG2[0], s[0]), new(bn256.G2).ScalarMult(pk.PartG2[1], s[1]), new(bn256.G2).ScalarBaseMult(new(big.Int).Add(s[0], s[1]))} ct := make([][3]*bn256.G1, len(msp.Mat)) for i := 0; i < len(msp.Mat); i++ { for l := 0; l < 3; l++ { hs1, err := bn256.HashG1(msp.RowToAttrib[i] + " " + strconv.Itoa(l) + " 0") if err != nil { return nil, err } hs1.ScalarMult(hs1, s[0]) hs2, err := bn256.HashG1(msp.RowToAttrib[i] + " " + strconv.Itoa(l) + " 1") if err != nil { return nil, err } hs2.ScalarMult(hs2, s[1]) ct[i][l] = new(bn256.G1).Add(hs1, hs2) for j := 0; j < len(msp.Mat[0]); j++ { hs1, err = bn256.HashG1("0 " + strconv.Itoa(j) + " " + strconv.Itoa(l) + " 0") if err != nil { return nil, err } hs1.ScalarMult(hs1, s[0]) hs2, err = bn256.HashG1("0 " + strconv.Itoa(j) + " " + strconv.Itoa(l) + " 1") if err != nil { return nil, err } hs2.ScalarMult(hs2, s[1]) hsToM := new(bn256.G1).Add(hs1, hs2) pow := new(big.Int).Set(msp.Mat[i][j]) if pow.Sign() == -1 { pow.Neg(pow) hsToM.ScalarMult(hsToM, pow) hsToM.Neg(hsToM) } else { hsToM.ScalarMult(hsToM, pow) } ct[i][l].Add(ct[i][l], hsToM) } } } ctPrime := new(bn256.GT).ScalarMult(pk.PartGT[0], s[0]) ctPrime.Add(ctPrime, new(bn256.GT).ScalarMult(pk.PartGT[1], s[1])) ctPrime.Add(ctPrime, keyGt) return &FAMECipher{Ct0: ct0, Ct: ct, CtPrime: ctPrime, Msp: msp, SymEnc: symEnc, Iv: iv}, nil } // FAMEAttribKeys represents keys corresponding to attributes possessed by // an entity and used for decrypting in a FAME scheme. type FAMEAttribKeys struct { K0 [3]*bn256.G2 K [][3]*bn256.G1 KPrime [3]*bn256.G1 AttribToI map[string]int } // GenerateAttribKeys given a set of attributes gamma and the master secret key // generates keys that can be used for the decryption of any ciphertext encoded // with a policy for which attributes gamma are sufficient. func (a *FAME) GenerateAttribKeys(gamma []string, sk *FAMESecKey) (*FAMEAttribKeys, error) { sampler := sample.NewUniform(a.P) r, err := data.NewRandomVector(2, sampler) if err != nil { return nil, err } sigma, err := data.NewRandomVector(len(gamma), sampler) if err != nil { return nil, err } pow0 := new(big.Int).Mul(sk.PartInt[2], r[0]) pow0.Mod(pow0, a.P) pow1 := new(big.Int).Mul(sk.PartInt[3], r[1]) pow1.Mod(pow1, a.P) pow2 := new(big.Int).Add(r[0], r[1]) pow2.Mod(pow2, a.P) k0 := [3]*bn256.G2{new(bn256.G2).ScalarBaseMult(pow0), new(bn256.G2).ScalarBaseMult(pow1), new(bn256.G2).ScalarBaseMult(pow2)} a0Inv := new(big.Int).ModInverse(sk.PartInt[0], a.P) a1Inv := new(big.Int).ModInverse(sk.PartInt[1], a.P) aInv := [2]*big.Int{a0Inv, a1Inv} k := make([][3]*bn256.G1, len(gamma)) attribToI := make(map[string]int) for i, y := range gamma { k[i] = [3]*bn256.G1{new(bn256.G1), new(bn256.G1), new(bn256.G1)} gSigma := new(bn256.G1).ScalarBaseMult(sigma[i]) for t := 0; t < 2; t++ { hs0, err := bn256.HashG1(y + " 0 " + strconv.Itoa(t)) if err != nil { return nil, err } hs0.ScalarMult(hs0, pow0) hs1, err := bn256.HashG1(y + " 1 " + strconv.Itoa(t)) if err != nil { return nil, err } hs1.ScalarMult(hs1, pow1) hs2, err := bn256.HashG1(y + " 2 " + strconv.Itoa(t)) if err != nil { return nil, err } hs2.ScalarMult(hs2, pow2) k[i][t].Add(hs0, hs1) k[i][t].Add(k[i][t], hs2) k[i][t].Add(k[i][t], gSigma) k[i][t].ScalarMult(k[i][t], aInv[t]) } k[i][2].ScalarBaseMult(sigma[i]) k[i][2].Neg(k[i][2]) attribToI[y] = i } sigmaPrime, err := sampler.Sample() if err != nil { return nil, err } gSigmaPrime := new(bn256.G1).ScalarBaseMult(sigmaPrime) k2 := [3]*bn256.G1{new(bn256.G1), new(bn256.G1), new(bn256.G1)} for t := 0; t < 2; t++ { hs0, err := bn256.HashG1("0 0 0 " + strconv.Itoa(t)) if err != nil { return nil, err } hs0.ScalarMult(hs0, pow0) hs1, err := bn256.HashG1("0 0 1 " + strconv.Itoa(t)) if err != nil { return nil, err } hs1.ScalarMult(hs1, pow1) hs2, err := bn256.HashG1("0 0 2 " + strconv.Itoa(t)) if err != nil { return nil, err } hs2.ScalarMult(hs2, pow2) k2[t].Add(hs0, hs1) k2[t].Add(k2[t], hs2) k2[t].Add(k2[t], gSigmaPrime) k2[t].ScalarMult(k2[t], aInv[t]) k2[t].Add(k2[t], sk.PartG1[t]) } k2[2].ScalarBaseMult(sigmaPrime) k2[2].Neg(k2[2]) k2[2].Add(k2[2], sk.PartG1[2]) return &FAMEAttribKeys{K0: k0, K: k, KPrime: k2, AttribToI: attribToI}, nil } // Decrypt takes as an input a cipher and an FAMEAttribKeys and tries to decrypt // the cipher. This is possible only if the set of possessed attributes (and // corresponding keys FAMEAttribKeys) suffices the encryption policy of the // cipher. If this is not possible, an error is returned. func (a *FAME) Decrypt(cipher *FAMECipher, key *FAMEAttribKeys, pk *FAMEPubKey) (string, error) { // find out which attributes are owned attribMap := make(map[string]bool) for k := range key.AttribToI { attribMap[k] = true } countAttrib := 0 for i := 0; i < len(cipher.Msp.Mat); i++ { if attribMap[cipher.Msp.RowToAttrib[i]] { countAttrib++ } } // create a matrix of needed keys preMatForKey := make([]data.Vector, countAttrib) ctForKey := make([][3]*bn256.G1, countAttrib) rowToAttrib := make([]string, countAttrib) countAttrib = 0 for i := 0; i < len(cipher.Msp.Mat); i++ { if attribMap[cipher.Msp.RowToAttrib[i]] { preMatForKey[countAttrib] = cipher.Msp.Mat[i] ctForKey[countAttrib] = cipher.Ct[i] rowToAttrib[countAttrib] = cipher.Msp.RowToAttrib[i] countAttrib++ } } matForKey, err := data.NewMatrix(preMatForKey) if err != nil { return "", fmt.Errorf("the provided cipher is faulty") } // matForKey may have a len of 0 if there is a single condition if len(matForKey) == 0 { return "", fmt.Errorf("provided key is not sufficient for decryption") } // get a combination alpha of keys needed to decrypt // matForKey may have a len of 0 if there is a single condition if len(matForKey) == 0 { return "", fmt.Errorf("provided key is not sufficient for decryption") } oneVec := data.NewConstantVector(len(matForKey[0]), big.NewInt(0)) oneVec[0].SetInt64(1) alpha, err := data.GaussianEliminationSolver(matForKey.Transpose(), oneVec, a.P) if err != nil { return "", fmt.Errorf("provided key is not sufficient for decryption") } // get a CBC key needed for the decryption of msg keyGt := new(bn256.GT).Set(cipher.CtPrime) ctProd := new([3]*bn256.G1) keyProd := new([3]*bn256.G1) for j := 0; j < 3; j++ { ctProd[j] = new(bn256.G1).ScalarBaseMult(big.NewInt(0)) keyProd[j] = new(bn256.G1).ScalarBaseMult(big.NewInt(0)) for i, e := range rowToAttrib { ctProd[j].Add(ctProd[j], new(bn256.G1).ScalarMult(ctForKey[i][j], alpha[i])) keyProd[j].Add(keyProd[j], new(bn256.G1).ScalarMult(key.K[key.AttribToI[e]][j], alpha[i])) } keyProd[j].Add(keyProd[j], key.KPrime[j]) ctPairing := bn256.Pair(ctProd[j], key.K0[j]) keyPairing := bn256.Pair(keyProd[j], cipher.Ct0[j]) keyPairing.Neg(keyPairing) keyGt.Add(keyGt, ctPairing) keyGt.Add(keyGt, keyPairing) } keyCBC := sha256.Sum256([]byte(keyGt.String())) c, err := aes.NewCipher(keyCBC[:]) if err != nil { return "", err } msgPad := make([]byte, len(cipher.SymEnc)) decrypter := cbc.NewCBCDecrypter(c, cipher.Iv) decrypter.CryptBlocks(msgPad, cipher.SymEnc) // unpad the message padLen := int(msgPad[len(msgPad)-1]) if (len(msgPad) - padLen) < 0 { return "", fmt.Errorf("failed to decrypt") } msgByte := msgPad[0:(len(msgPad) - padLen)] return string(msgByte), nil } ================================================ FILE: abe/fame_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package abe_test import ( "testing" "github.com/fentec-project/gofe/abe" "github.com/stretchr/testify/assert" ) func TestFAME(t *testing.T) { // create a new FAME struct with the universe of attributes // denoted by integer a := abe.NewFAME() // generate a public key and a secret key for the scheme pubKey, secKey, err := a.GenerateMasterKeys() if err != nil { t.Fatalf("Failed to generate master keys: %v", err) } // create a message to be encrypted msg := "Attack at dawn!" // create a msp struct out of a boolean expression representing the // policy specifying which attributes are needed to decrypt the ciphertext; // the boolean expression is a string of attributes joined by AND and OR // hence the names of the attributes should not include "AND" or "OR" // as a substring and '(' or ')' as a character // note that safety of the encryption is only proved if the mapping // msp.RowToAttrib from the rows of msp.Mat to attributes is injective, i.e. // only boolean expressions in which each attribute appears at most once // are allowed - if expressions with multiple appearances of an attribute // are needed, then this attribute can be split into more sub-attributes msp, err := abe.BooleanToMSP("((0 AND 1) OR (2 AND 3)) AND 5", false) if err != nil { t.Fatalf("Failed to generate the policy: %v", err) } // encrypt the message msg with the decryption policy specified by the // msp structure cipher, err := a.Encrypt(msg, msp, pubKey) if err != nil { t.Fatalf("Failed to encrypt: %v", err) } // define a set of attributes (a subset of the universe of attributes) // that an entity possesses gamma := []string{"0", "2", "3", "5"} // generate keys for decryption for an entity with // attributes gamma keys, err := a.GenerateAttribKeys(gamma, secKey) if err != nil { t.Fatalf("Failed to generate keys: %v", err) } // decrypt the ciphertext with the keys of an entity // that has sufficient attributes msgCheck, err := a.Decrypt(cipher, keys, pubKey) if err != nil { t.Fatalf("Failed to decrypt: %v", err) } assert.Equal(t, msg, msgCheck) // define a set of attributes (a subset of the universe of attributes) // that an entity possesses gammaInsuff := []string{"1", "3", "5"} // generate keys for decryption for an entity with // attributes gammaInsuff keysInsuff, err := a.GenerateAttribKeys(gammaInsuff, secKey) if err != nil { t.Fatalf("Failed to generate keys: %v", err) } // try to decrypt the ciphertext with the keys of an entity // that has insufficient attributes _, err = a.Decrypt(cipher, keysInsuff, pubKey) assert.Error(t, err) mspSingleCondition, err := abe.BooleanToMSP("0", false) if err != nil { t.Fatalf("Failed to generate the policy: %v", err) } // encrypt the message msg with the decryption policy specified by the // msp structure cipherSingleCondition, err := a.Encrypt(msg, mspSingleCondition, pubKey) if err != nil { t.Fatalf("Failed to encrypt: %v", err) } msgCheckSingleCondition, err := a.Decrypt(cipherSingleCondition, keys, pubKey) if err != nil { t.Fatalf("Failed to decrypt: %v", err) } assert.Equal(t, msg, msgCheckSingleCondition) _, err = a.Decrypt(cipherSingleCondition, keysInsuff, pubKey) assert.Error(t, err) // test with Single UUID mspSingleUUID, err := abe.BooleanToMSP("123e4567-e89b-12d3-a456-426655440000", false) if err != nil { t.Fatalf("Failed to generate the policy: %v", err) } cipherSingleUUID, err := a.Encrypt(msg, mspSingleUUID, pubKey) if err != nil { t.Fatalf("Failed to encrypt: %v", err) } // define a set of attributes (a subset of the universe of attributes) // that an entity possesses gammaUUID := []string{"123e4567-e89b-12d3-a456-426655440000", "123e4567-e89b-12d3-a456-4266554400001"} // generate keys for decryption for an entity with // attributes gamma keysUUID, err := a.GenerateAttribKeys(gammaUUID, secKey) if err != nil { t.Fatalf("Failed to generate keys: %v", err) } // decrypt the ciphertext with the keys of an entity // that has sufficient attributes msgCheckSingleUUID, err := a.Decrypt(cipherSingleUUID, keysUUID, pubKey) if err != nil { t.Fatalf("Failed to decrypt: %v", err) } assert.Equal(t, msg, msgCheckSingleUUID) // define a set of attributes (a subset of the universe of attributes) // that an entity possesses gammaInsuffUUID := []string{"123e4567-e89b-12d3-a456-426655440099"} // generate keys for decryption for an entity with // attributes gammaInsuff keysInsuffUUID, err := a.GenerateAttribKeys(gammaInsuffUUID, secKey) if err != nil { t.Fatalf("Failed to generate keys: %v", err) } // try to decrypt the ciphertext with the keys of an entity // that has insufficient attributes _, err = a.Decrypt(cipherSingleUUID, keysInsuffUUID, pubKey) assert.Error(t, err) // // test with Multi UUID mspMultiUUID, err := abe.BooleanToMSP("123e4567-e89b-12d3-a456-426655440000 OR 123e4567-e89b-12d3-a456-426655440001", false) if err != nil { t.Fatalf("Failed to generate the policy: %v", err) } cipherMultiUUID, err := a.Encrypt(msg, mspMultiUUID, pubKey) if err != nil { t.Fatalf("Failed to encrypt: %v", err) } // decrypt the ciphertext with the keys of an entity // that has sufficient attributes msgCheckMultiUUID, err := a.Decrypt(cipherMultiUUID, keysUUID, pubKey) if err != nil { t.Fatalf("Failed to decrypt: %v", err) } assert.Equal(t, msg, msgCheckMultiUUID) // try to decrypt the ciphertext with the keys of an entity // that has insufficient attributes _, err = a.Decrypt(cipherMultiUUID, keysInsuffUUID, pubKey) assert.Error(t, err) } ================================================ FILE: abe/gpsw.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package abe import ( "crypto/aes" cbc "crypto/cipher" "crypto/rand" "crypto/sha256" "fmt" "math/big" "io" "strconv" "github.com/fentec-project/bn256" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/sample" ) // This is a key policy (KP) attribute based (ABE) scheme based on // Goyal, Pandey, Sahai, Waters: // "Attribute-Based Encryption for Fine-Grained Access Control of // Encrypted Data" // // We abbreviated it GPSW scheme to honor the authors. This scheme // enables encrypting data associated with a set of attributes and // generating keys associated with boolean expressions determining the // policy. A user that owns such a key can decrypt the ciphertext // only if the attributes associated to the ciphertext satisfy the //policy associated to the key. // This scheme is a PUBLIC-KEY scheme - no master secret key is needed // to encrypt the messages. // GPSWParams represents configuration parameters for the GPSW ABE-scheme instance. type GPSWParams struct { L int // number of attributes P *big.Int // order of the elliptic curve } // GPSW represents an GPSW ABE-scheme. type GPSW struct { Params *GPSWParams } // NewGPSW configures a new instance of the scheme. // It accepts l the number of attributes possibly used in // the scheme. Attributes' names will be considered as // elements of a set {0, 1,..., l-1}. func NewGPSW(l int) *GPSW { return &GPSW{Params: &GPSWParams{ L: l, // number of attributes in the whole universe P: bn256.Order, // the order of the pairing groups }} } // GPSWPubKey represents a public key of the GPSW ABE-scheme. type GPSWPubKey struct { T data.VectorG2 Y *bn256.GT } // GenerateMasterKeys generates a new set of public keys, needed // for encrypting data, and secret keys needed for generating keys // for decryption. func (a *GPSW) GenerateMasterKeys() (*GPSWPubKey, data.Vector, error) { sampler := sample.NewUniform(a.Params.P) sk, err := data.NewRandomVector(a.Params.L+1, sampler) if err != nil { return nil, nil, err } t := sk[:a.Params.L].MulG2() y := new(bn256.GT).ScalarBaseMult(sk[a.Params.L]) return &GPSWPubKey{T: t, Y: y}, sk, nil } // GPSWCipher represents a ciphertext of the GPSW ABE-scheme. type GPSWCipher struct { Gamma []int // the set of attributes that can be used for policy of decryption AttribToI map[int]int // a map that connects the attributes in gamma with elements of e E0 *bn256.GT // the first part of the encryption E data.VectorG2 // the second part of the encryption SymEnc []byte // symmetric encryption of the message Iv []byte // initialization vector for symmetric encryption } // Encrypt takes as an input a message msg given as a string, gamma a set (slice) // of attributes that will be associated with the encryption and a public // key pk. It returns an encryption of msg. In case of a failed procedure an // error is returned. func (a *GPSW) Encrypt(msg string, gamma interface{}, pk *GPSWPubKey) (*GPSWCipher, error) { var gammaI []int switch gamma.(type) { default: return nil, fmt.Errorf("attributes should be of type []int or []string of integers") case []int: gammaI = gamma.([]int) case []string: gammaI = make([]int, len(gamma.([]string))) for i, e := range gamma.([]string) { att, err := strconv.Atoi(e) if err != nil { return nil, err } gammaI[i] = att } } // msg is encrypted using CBC, with a random key that is encapsulated // with GPSW _, keyGt, err := bn256.RandomGT(rand.Reader) if err != nil { return nil, err } keyCBC := sha256.Sum256([]byte(keyGt.String())) c, err := aes.NewCipher(keyCBC[:]) if err != nil { return nil, err } iv := make([]byte, c.BlockSize()) _, err = io.ReadFull(rand.Reader, iv) if err != nil { return nil, err } encrypterCBC := cbc.NewCBCEncrypter(c, iv) msgByte := []byte(msg) // message is padded according to pkcs7 standard padLen := c.BlockSize() - (len(msgByte) % c.BlockSize()) msgPad := make([]byte, len(msgByte)+padLen) copy(msgPad, msgByte) for i := len(msgByte); i < len(msgPad); i++ { msgPad[i] = byte(padLen) } symEnc := make([]byte, len(msgPad)) encrypterCBC.CryptBlocks(symEnc, msgPad) // encapsulate the key with GPSW sampler := sample.NewUniform(a.Params.P) s, err := sampler.Sample() if err != nil { return nil, err } e0 := new(bn256.GT).Add(keyGt, new(bn256.GT).ScalarMult(pk.Y, s)) e := make(data.VectorG2, len(gammaI)) attribToI := make(map[int]int) for i, el := range gammaI { e[i] = new(bn256.G2).ScalarMult(pk.T[el], s) attribToI[el] = i } return &GPSWCipher{Gamma: gammaI, AttribToI: attribToI, E0: e0, E: e, SymEnc: symEnc, Iv: iv}, nil } // GPSWKey represents a key structure for decrypting a ciphertext. It includes // a msp structure (policy) associated with the key and a vector D representing // the main part of the key. type GPSWKey struct { Msp *MSP D data.VectorG1 } // GeneratePolicyKey given a monotone span program (MSP) msp and the vector of secret // keys produces an ABE key associated with the policy given by MSP. In particular, // this key can be used to decrypt any cipertext associated with attributes that // satisfy given policy. func (a *GPSW) GeneratePolicyKey(msp *MSP, sk data.Vector) (*GPSWKey, error) { if len(msp.Mat) == 0 || len(msp.Mat[0]) == 0 { return nil, fmt.Errorf("empty msp matrix") } if len(sk) != (a.Params.L + 1) { return nil, fmt.Errorf("the secret key has wrong length") } u, err := getSum(sk[a.Params.L], a.Params.P, len(msp.Mat[0])) if err != nil { return nil, err } key := make(data.VectorG1, len(msp.Mat)) for i := 0; i < len(msp.Mat); i++ { attrib, err := strconv.Atoi(msp.RowToAttrib[i]) if err != nil { return nil, err } if 0 > attrib || a.Params.L <= attrib { return nil, fmt.Errorf("attributes of msp not in the universe of a") } tMapIInv := new(big.Int).ModInverse(sk[attrib], a.Params.P) matTimesU, err := msp.Mat[i].Dot(u) if err != nil { return nil, err } pow := new(big.Int).Mul(tMapIInv, matTimesU) pow.Mod(pow, a.Params.P) key[i] = new(bn256.G1).ScalarBaseMult(pow) } return &GPSWKey{Msp: msp, D: key}, nil } // getSum is a helping function that given integers y, p and d generates a // random d dimensional vector over Z_p whose entries sum to y in Z_p. func getSum(y *big.Int, p *big.Int, d int) (data.Vector, error) { sampler := sample.NewUniform(p) ret, err := data.NewRandomVector(d, sampler) if err != nil { return nil, err } sum := big.NewInt(0) for i := 0; i < d-1; i++ { sum.Add(sum, ret[i]) sum.Mod(sum, p) } ret[d-1] = new(big.Int).Sub(y, sum) ret[d-1].Mod(ret[d-1], p) return ret, nil } // Decrypt takes as an input a cipher and an GPSWKey key and tries to decrypt // the cipher. If the GPSWKey is properly generated, this is possible if and // only if the set of attributes associated with the ciphertext satisfies the // policy (boolean expression) of the key. This is if and only if the // rows of the msp matrix in the key associated with the attributes of the // ciphertext span the vector [1, 1,..., 1]. If this is not possible, an //error is returned. func (a *GPSW) Decrypt(cipher *GPSWCipher, key *GPSWKey) (string, error) { // get intersection of gamma and attributes used in the key policy gammaMap := make(map[int]bool) for _, e := range cipher.Gamma { gammaMap[e] = true } intersection := make([]int, 0) mat := make(data.Matrix, 0) d := make(data.VectorG1, 0) for i := 0; i < len(key.Msp.Mat); i++ { attrib, err := strconv.Atoi(key.Msp.RowToAttrib[i]) if err != nil { return "", err } if gammaMap[attrib] { intersection = append(intersection, attrib) mat = append(mat, key.Msp.Mat[i]) d = append(d, key.D[i]) } } // get a combination alpha of keys needed to decrypt ones := data.NewConstantVector(len(mat[0]), big.NewInt(1)) alpha, err := data.GaussianEliminationSolver(mat.Transpose(), ones, a.Params.P) if err != nil { return "", fmt.Errorf("the provided key is not sufficient for the decryption") } // get a CBC key needed for the decryption of msg keyGt := new(bn256.GT).Set(cipher.E0) for i := 0; i < len(alpha); i++ { pair := bn256.Pair(d[i], cipher.E[cipher.AttribToI[intersection[i]]]) pair.ScalarMult(pair, alpha[i]) pair.Neg(pair) keyGt.Add(keyGt, pair) } keyCBC := sha256.Sum256([]byte(keyGt.String())) c, err := aes.NewCipher(keyCBC[:]) if err != nil { return "", err } msgPad := make([]byte, len(cipher.SymEnc)) decrypter := cbc.NewCBCDecrypter(c, cipher.Iv) decrypter.CryptBlocks(msgPad, cipher.SymEnc) // unpad the message padLen := int(msgPad[len(msgPad)-1]) if (len(msgPad) - padLen) < 0 { return "", fmt.Errorf("failed to decrypt") } msgByte := msgPad[0:(len(msgPad) - padLen)] return string(msgByte), nil } ================================================ FILE: abe/gpsw_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package abe_test import ( "testing" "github.com/fentec-project/gofe/abe" "github.com/fentec-project/gofe/data" "github.com/stretchr/testify/assert" ) func TestGPSW(t *testing.T) { // create a new GPSW struct with the universe of l possible // attributes (attributes are denoted by the integers in [0, l)) l := 10 a := abe.NewGPSW(l) // generate a public key and a secret key for the scheme pubKey, secKey, err := a.GenerateMasterKeys() if err != nil { t.Fatalf("Failed to generate master keys: %v", err) } // create two messages to be encrypted msg1 := "Attack at dawn!" msg2 := "More chocolate!" // define a set of attributes (a subset of the universe of attributes) // that will be associated with the encryptions gamma1 := []int{0, 4, 5} // could be given also as []string{"0", "4", "5"} gamma2 := []int{0, 1, 4} // could be given also as []string{"0", "1", "4"} // encrypt the first message with associated attributes gamma1 cipher1, err := a.Encrypt(msg1, gamma1, pubKey) if err != nil { t.Fatalf("Failed to encrypt: %v", err) } // encrypt the second message with associated attributes gamma2 cipher2, err := a.Encrypt(msg2, gamma2, pubKey) if err != nil { t.Fatalf("Failed to encrypt: %v", err) } // create a msp struct out of a boolean expression representing the // policy specifying which attributes are needed to decrypt the ciphertext; // the boolean expression is a string of attributes joined by AND and OR // where attributes are integers from the interval [0, l) // note that the safety of the encryption is only proved if the mapping // msp.RowToAttrib from the rows of msp.Mat to attributes is injective, i.e. // only boolean expressions in which each attribute appears at most once // are allowed - if expressions with multiple appearances of an attribute // are needed, then this attribute can be split into more sub-attributes msp, err := abe.BooleanToMSP("(1 OR 4) AND (2 OR (0 AND 5))", true) if err != nil { t.Fatalf("Failed to generate the policy: %v", err) } // generate a key for decryption that correspond to provided msp struct, // i.e. a key that can decrypt a message iff the attributes associated // with the ciphertext satisfy the boolean expression abeKey, err := a.GeneratePolicyKey(msp, secKey) if err != nil { t.Fatalf("Failed to generate keys: %v", err) } // test if error is returned when a bad Msp struct is given emptyMsp := &abe.MSP{Mat: make(data.Matrix, 0), RowToAttrib: make([]string, 0)} _, err = a.GeneratePolicyKey(emptyMsp, secKey) assert.Error(t, err) // decrypt the first ciphertext with abeKey msgCheck, err := a.Decrypt(cipher1, abeKey) if err != nil { t.Fatalf("Failed to decrypt: %v", err) } assert.Equal(t, msg1, msgCheck) // try to decrypt the second ciphertext but fail with abeKey _, err = a.Decrypt(cipher2, abeKey) assert.Error(t, err) } ================================================ FILE: abe/ma-abe.go ================================================ /* * Copyright (c) 2021 XLAB d.o.o * * 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. */ package abe import ( "crypto/aes" cbc "crypto/cipher" "crypto/rand" "crypto/sha256" "fmt" "math/big" "io" "github.com/fentec-project/bn256" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/sample" ) // This is a ciphertext policy (CP) multi-authority (MA) attribute based // encryption (ABE) scheme based on the paper "Decentralizing Attribute-Based // Encryption" by Allison Lewko and Brent Waters, accessible at // (https://eprint.iacr.org/2010/351.pdf). // // This scheme enables encryption based on a boolean expression determining // which attributes are needed for an entity to be able to decrypt, where the // attributes can be spread across many different authorities, eliminating the // need for a central authority. Secret keys, each connected to a single // attribute, are generated by the relevant authorities, such that only a set // of keys whose attributes are sufficient according to the boolean formula can // decrypt the message. // MAABE represents a MAABE scheme. type MAABE struct { P *big.Int G1 *bn256.G1 G2 *bn256.G2 Gt *bn256.GT } // NewMAABE configures a new instance of the scheme. func NewMAABE() *MAABE { gen1 := new(bn256.G1).ScalarBaseMult(big.NewInt(1)) gen2 := new(bn256.G2).ScalarBaseMult(big.NewInt(1)) return &MAABE{ P: bn256.Order, G1: gen1, G2: gen2, Gt: bn256.Pair(gen1, gen2), } } // MAABEPubKey represents a public key for an authority. type MAABEPubKey struct { Attribs []string EggToAlpha map[string]*bn256.GT GToY map[string]*bn256.G2 } // MAABESecKey represents a secret key for an authority. type MAABESecKey struct { Attribs []string Alpha map[string]*big.Int Y map[string]*big.Int } // MAABEAuth represents an authority in the MAABE scheme. type MAABEAuth struct { ID string Maabe *MAABE Pk *MAABEPubKey Sk *MAABESecKey } // NewMAABEAuth configures a new instance of an authority and generates its // public and secret keys for the given set of attributes. In case of a failed // procedure an error is returned. func (a *MAABE) NewMAABEAuth(id string, attribs []string) (*MAABEAuth, error) { numattrib := len(attribs) // sanity checks if numattrib == 0 { return nil, fmt.Errorf("empty set of authority attributes") } if len(id) == 0 { return nil, fmt.Errorf("empty id string") } // rand generator sampler := sample.NewUniform(a.P) // generate seckey alphaI, err := data.NewRandomVector(numattrib, sampler) if err != nil { return nil, err } yI, err := data.NewRandomVector(numattrib, sampler) if err != nil { return nil, err } alpha := make(map[string]*big.Int) y := make(map[string]*big.Int) for i, at := range attribs { alpha[at] = alphaI[i] y[at] = yI[i] } // generate pubkey eggToAlpha := make(map[string]*bn256.GT) gToY := make(map[string]*bn256.G2) for _, at := range attribs { eggToAlpha[at] = new(bn256.GT).ScalarMult(a.Gt, alpha[at]) gToY[at] = new(bn256.G2).ScalarMult(a.G2, y[at]) } sk := &MAABESecKey{Attribs: attribs, Alpha: alpha, Y: y} pk := &MAABEPubKey{Attribs: attribs, EggToAlpha: eggToAlpha, GToY: gToY} return &MAABEAuth{ ID: id, Maabe: a, Pk: pk, Sk: sk, }, nil } // PubKeys is a getter function that returns a copy of the authority's public // keys. func (auth *MAABEAuth) PubKeys() *MAABEPubKey { newEggToAlpha := make(map[string]*bn256.GT) newGToY := make(map[string]*bn256.G2) newAttribs := make([]string, len(auth.Pk.Attribs)) copy(newAttribs, auth.Pk.Attribs) for at, gt := range auth.Pk.EggToAlpha { newEggToAlpha[at] = new(bn256.GT).Set(gt) } for at, g2 := range auth.Pk.GToY { newGToY[at] = new(bn256.G2).Set(g2) } return &MAABEPubKey{ Attribs: newAttribs, EggToAlpha: newEggToAlpha, GToY: newGToY, } } // AddAttribute generates public and secret keys for a new attribute that is // given as input. In case of a failed procedure an error is returned, and nil // otherwise. func (auth *MAABEAuth) AddAttribute(attrib string) error { // sanity checks if len(attrib) == 0 { return fmt.Errorf("attribute cannot be an empty string") } if auth.Maabe == nil { return fmt.Errorf("MAABE struct cannot be nil") } // attribute should not already exist, separate function if auth.Sk.Alpha[attrib] != nil || auth.Sk.Y[attrib] != nil { return fmt.Errorf("attribute already exists") } // generate secret key sampler := sample.NewUniform(auth.Maabe.P) skVals, err := data.NewRandomVector(2, sampler) if err != nil { return err } alpha := skVals[0] y := skVals[1] // generate public key eggToAlpha := new(bn256.GT).ScalarMult(auth.Maabe.Gt, alpha) gToY := new(bn256.G2).ScalarMult(auth.Maabe.G2, y) // add keys to authority auth.Sk.Alpha[attrib] = alpha auth.Sk.Y[attrib] = y auth.Pk.EggToAlpha[attrib] = eggToAlpha auth.Pk.GToY[attrib] = gToY auth.Sk.Attribs = append(auth.Sk.Attribs, attrib) auth.Pk.Attribs = append(auth.Pk.Attribs, attrib) return nil } // RegenerateKey generates public and secret keys for an already existing // attribute that is given as input. In case of a failed procedure an error is // returned. It is meant to be used in case only a part of the authority's // secret keys get compromised. Note that the new public keys have to be // distributed and messages that were encrypted with a policy that contains // this attribute have to also be reencrypted. func (auth *MAABEAuth) RegenerateKey(attrib string) error { // sanity checks if len(attrib) == 0 { return fmt.Errorf("attribute cannot be an empty string") } if auth.Maabe == nil { return fmt.Errorf("MAABE struct cannot be nil") } // attribute must already exist if auth.Sk.Alpha[attrib] == nil || auth.Sk.Y[attrib] == nil { return fmt.Errorf("attribute does not exist yet") } // generate secret key sampler := sample.NewUniform(auth.Maabe.P) skVals, err := data.NewRandomVector(2, sampler) if err != nil { return err } alpha := skVals[0] y := skVals[1] // generate public key eggToAlpha := new(bn256.GT).ScalarMult(auth.Maabe.Gt, alpha) gToY := new(bn256.G2).ScalarMult(auth.Maabe.G2, y) // add keys to authority auth.Sk.Alpha[attrib] = alpha auth.Sk.Y[attrib] = y auth.Pk.EggToAlpha[attrib] = eggToAlpha auth.Pk.GToY[attrib] = gToY return nil } // MAABECipher represents a ciphertext of a MAABE scheme. type MAABECipher struct { C0 *bn256.GT C1x map[string]*bn256.GT C2x map[string]*bn256.G2 C3x map[string]*bn256.G2 Msp *MSP SymEnc []byte // symmetric encryption of the string message Iv []byte // initialization vector for symmetric encryption } // Encrypt takes an input message in string form, a MSP struct representing the // decryption policy and a list of public keys of the relevant authorities. It // returns a ciphertext consisting of an AES encrypted message with the secret // key encrypted according to the MAABE scheme. In case of a failed procedure // an error is returned. func (a *MAABE) Encrypt(msg string, msp *MSP, pks []*MAABEPubKey) (*MAABECipher, error) { // sanity checks if len(msp.Mat) == 0 || len(msp.Mat[0]) == 0 { return nil, fmt.Errorf("empty msp matrix") } mspRows := msp.Mat.Rows() mspCols := msp.Mat.Cols() attribs := make(map[string]bool) for _, i := range msp.RowToAttrib { if attribs[i] { return nil, fmt.Errorf("some attributes correspond to" + "multiple rows of the MSP struct, the scheme is not secure") } attribs[i] = true } if len(msg) == 0 { return nil, fmt.Errorf("message cannot be empty") } // msg is encrypted with AES-CBC with a random key that is encrypted with // MA-ABE // generate secret key _, symKey, err := bn256.RandomGT(rand.Reader) if err != nil { return nil, err } // generate new AES-CBC params keyCBC := sha256.Sum256([]byte(symKey.String())) cipherAES, err := aes.NewCipher(keyCBC[:]) if err != nil { return nil, err } iv := make([]byte, cipherAES.BlockSize()) _, err = io.ReadFull(rand.Reader, iv) if err != nil { return nil, err } encrypterCBC := cbc.NewCBCEncrypter(cipherAES, iv) // interpret msg as a byte array and pad it according to PKCS7 standard msgByte := []byte(msg) padLen := cipherAES.BlockSize() - (len(msgByte) % cipherAES.BlockSize()) msgPad := make([]byte, len(msgByte) + padLen) copy(msgPad, msgByte) for i := len(msgByte); i < len(msgPad); i++ { msgPad[i] = byte(padLen) } // encrypt data symEnc := make([]byte, len(msgPad)) encrypterCBC.CryptBlocks(symEnc, msgPad) // now encrypt symKey with MA-ABE // rand generator sampler := sample.NewUniform(a.P) // pick random vector v with random s as first element v, err := data.NewRandomVector(mspCols, sampler) if err != nil { return nil, err } s := v[0] if err != nil { return nil, err } lambdaI, err := msp.Mat.MulVec(v) if err != nil { return nil, err } if len(lambdaI) != mspRows { return nil, fmt.Errorf("wrong lambda len") } lambda := make(map[string]*big.Int) for i, at := range msp.RowToAttrib { lambda[at] = lambdaI[i] } // pick random vector w with 0 as first element w, err := data.NewRandomVector(mspCols, sampler) if err != nil { return nil, err } w[0] = big.NewInt(0) omegaI, err := msp.Mat.MulVec(w) if err != nil { return nil, err } if len(omegaI) != mspRows { return nil, fmt.Errorf("wrong omega len") } omega := make(map[string]*big.Int) for i, at := range msp.RowToAttrib { omega[at] = omegaI[i] } // calculate ciphertext c0 := new(bn256.GT).Add(symKey, new(bn256.GT).ScalarMult(a.Gt, s)) c1 := make(map[string]*bn256.GT) c2 := make(map[string]*bn256.G2) c3 := make(map[string]*bn256.G2) // get randomness rI, err := data.NewRandomVector(mspRows, sampler) r := make(map[string]*big.Int) for i, at := range msp.RowToAttrib { r[at] = rI[i] } if err != nil { return nil, err } for _, at := range msp.RowToAttrib { // find the correct pubkey foundPK := false for _, pk := range pks { if pk.EggToAlpha[at] != nil { // CAREFUL: negative numbers do not play well with ScalarMult signLambda := lambda[at].Cmp(big.NewInt(0)) signOmega := omega[at].Cmp(big.NewInt(0)) var tmpLambda *bn256.GT var tmpOmega *bn256.G2 if signLambda >= 0 { tmpLambda = new(bn256.GT).ScalarMult(a.Gt, lambda[at]) } else { tmpLambda = new(bn256.GT).ScalarMult(new(bn256.GT).Neg(a.Gt), new(big.Int).Abs(lambda[at])) } if signOmega >= 0 { tmpOmega = new(bn256.G2).ScalarMult(a.G2, omega[at]) } else { tmpOmega = new(bn256.G2).ScalarMult(new(bn256.G2).Neg(a.G2), new(big.Int).Abs(omega[at])) } c1[at] = new(bn256.GT).Add(tmpLambda, new(bn256.GT).ScalarMult(pk.EggToAlpha[at], r[at])) c2[at] = new(bn256.G2).ScalarMult(a.G2, r[at]) c3[at] = new(bn256.G2).Add(new(bn256.G2).ScalarMult(pk.GToY[at], r[at]), tmpOmega) foundPK = true break } } if !foundPK { return nil, fmt.Errorf("attribute not found in any pubkey") } } return &MAABECipher{ C0: c0, C1x: c1, C2x: c2, C3x: c3, Msp: msp, SymEnc: symEnc, Iv: iv, }, nil } // MAABEKey represents a key corresponding to an attribute possessed by an // entity. They are issued by the relevant authorities and are used for // decryption in a MAABE scheme. type MAABEKey struct { Gid string Attrib string Key *bn256.G1 } // GenerateAttribKeys generates a list of attribute keys for the given user // (represented by its Global ID) that possesses the given list of attributes. // In case of a failed procedure an error is returned. The relevant authority // has to check that the entity actually possesses the attributes via some // other channel. func (auth *MAABEAuth) GenerateAttribKeys(gid string, attribs []string) ([]*MAABEKey, error) { // sanity checks if len(gid) == 0 { return nil, fmt.Errorf("GID cannot be empty") } if len(attribs) == 0 { return nil, fmt.Errorf("attribute cannot be empty") } if auth.Maabe == nil { return nil, fmt.Errorf("ma-abe scheme cannot be nil") } hash, err := bn256.HashG1(gid) if err != nil { return nil, err } ks := make([]*MAABEKey, len(attribs)) for i, at := range attribs { var k *bn256.G1 if auth.Sk.Alpha[at] != nil && auth.Sk.Y[at] != nil { k = new(bn256.G1).Add(new(bn256.G1).ScalarMult(auth.Maabe.G1, auth.Sk.Alpha[at]), new(bn256.G1).ScalarMult(hash, auth.Sk.Y[at])) ks[i] = &MAABEKey{ Gid: gid, Attrib: at, Key: k, } } else { return nil, fmt.Errorf("attribute not found in secret key") } } return ks, nil } // Decrypt takes a ciphertext in a MAABE scheme and a set of attribute keys // belonging to the same entity, and attempts to decrypt the cipher. This is // possible only if the set of possessed attributes/keys suffices the // decryption policy of the ciphertext. In case this is not possible or // something goes wrong an error is returned. func (a * MAABE) Decrypt(ct *MAABECipher, ks []*MAABEKey) (string, error) { // sanity checks if len(ks) == 0 { return "", fmt.Errorf("empty set of attribute keys") } gid := ks[0].Gid for _, k := range ks { if k.Gid != gid { return "", fmt.Errorf("not all GIDs are the same") } } // get hashed GID hash, err := bn256.HashG1(gid) if err != nil { return "", err } // find out which attributes are valid and extract them goodMatRows := make([]data.Vector, 0) goodAttribs := make([]string, 0) aToK := make(map[string]*MAABEKey) for _, k := range ks { aToK[k.Attrib] = k } for i, at := range ct.Msp.RowToAttrib { if aToK[at] != nil { goodMatRows = append(goodMatRows, ct.Msp.Mat[i]) goodAttribs = append(goodAttribs, at) } } goodMat, err := data.NewMatrix(goodMatRows) if err != nil { return "", err } //choose consts c_x, such that \sum c_x A_x = (1,0,...,0) // if they don't exist, keys are not ok goodCols := goodMat.Cols() if goodCols == 0 { return "", fmt.Errorf("no good matrix columns, most likely the keys contain no valid attribute") } one := data.NewConstantVector(goodCols, big.NewInt(0)) one[0] = big.NewInt(1) c, err := data.GaussianEliminationSolver(goodMat.Transpose(), one, a.P) if err != nil { return "", err } cx := make(map[string]*big.Int) for i, at := range goodAttribs { cx[at] = c[i] } // compute intermediate values eggLambda := make(map[string]*bn256.GT) for _, at := range goodAttribs { if ct.C1x[at] != nil && ct.C2x[at] != nil && ct.C3x[at] != nil { num := new(bn256.GT).Add(ct.C1x[at], bn256.Pair(hash, ct.C3x[at])) den := new(bn256.GT).Neg(bn256.Pair(aToK[at].Key, ct.C2x[at])) eggLambda[at] = new(bn256.GT).Add(num, den) } else { return "", fmt.Errorf("attribute %s not in ciphertext dicts", at) } } eggs := new(bn256.GT).ScalarBaseMult(big.NewInt(0)) for _, at := range goodAttribs { if eggLambda[at] != nil { sign := cx[at].Cmp(big.NewInt(0)) if sign == 1 { eggs.Add(eggs, new(bn256.GT).ScalarMult(eggLambda[at], cx[at])) } else if sign == -1 { eggs.Add(eggs, new(bn256.GT).ScalarMult(new(bn256.GT).Neg(eggLambda[at]), new(big.Int).Abs(cx[at]))) } } else { return "", fmt.Errorf("missing intermediate result") } } // calculate key for symmetric encryption symKey := new(bn256.GT).Add(ct.C0, new(bn256.GT).Neg(eggs)) // now decrypt message with it keyCBC := sha256.Sum256([]byte(symKey.String())) cipherAES, err := aes.NewCipher(keyCBC[:]) if err != nil { return "", err } msgPad := make([]byte, len(ct.SymEnc)) decrypter := cbc.NewCBCDecrypter(cipherAES, ct.Iv) decrypter.CryptBlocks(msgPad, ct.SymEnc) // unpad the message padLen := int(msgPad[len(msgPad)-1]) if (len(msgPad) - padLen) < 0 { return "", fmt.Errorf("failed to decrypt") } msgByte := msgPad[0:(len(msgPad) - padLen)] return string(msgByte), nil } ================================================ FILE: abe/ma-abe_test.go ================================================ package abe_test import ( "testing" "github.com/fentec-project/gofe/abe" "github.com/stretchr/testify/assert" ) func TestMAABE(t *testing.T) { // create new MAABE struct with Global Parameters maabe := abe.NewMAABE() // create three authorities, each with two attributes attribs1 := []string{"auth1:at1", "auth1:at2"} attribs2 := []string{"auth2:at1", "auth2:at2"} attribs3 := []string{"auth3:at1", "auth3:at2"} auth1, err:= maabe.NewMAABEAuth("auth1", attribs1) if err != nil { t.Fatalf("Failed generation authority %s: %v\n", "auth1", err) } auth2, err:= maabe.NewMAABEAuth("auth2", attribs2) if err != nil { t.Fatalf("Failed generation authority %s: %v\n", "auth2", err) } auth3, err:= maabe.NewMAABEAuth("auth3", attribs3) if err != nil { t.Fatalf("Failed generation authority %s: %v\n", "auth3", err) } // create a msp struct out of the boolean formula msp, err := abe.BooleanToMSP("((auth1:at1 AND auth2:at1) OR (auth1:at2 AND auth2:at2)) OR (auth3:at1 AND auth3:at2)", false) if err != nil { t.Fatalf("Failed to generate the policy: %v\n", err) } // define the set of all public keys we use pks := []*abe.MAABEPubKey{auth1.PubKeys(), auth2.PubKeys(), auth3.PubKeys()} // choose a message msg := "Attack at dawn!" // encrypt the message with the decryption policy in msp ct, err := maabe.Encrypt(msg, msp, pks) if err != nil { t.Fatalf("Failed to encrypt: %v\n", err) } // also check for empty message msgEmpty := "" _, err = maabe.Encrypt(msgEmpty, msp, pks) assert.Error(t, err) // use a pub keyring that is too small pksSmall := []*abe.MAABEPubKey{auth1.PubKeys()} _, err = maabe.Encrypt(msg, msp, pksSmall) assert.Error(t, err) // choose a single user's Global ID gid := "gid1" // authority 1 issues keys to user keys1, err := auth1.GenerateAttribKeys(gid, attribs1) if err != nil { t.Fatalf("Failed to generate attribute keys: %v\n", err) } key11, key12 := keys1[0], keys1[1] // authority 2 issues keys to user keys2, err := auth2.GenerateAttribKeys(gid, attribs2) if err != nil { t.Fatalf("Failed to generate attribute keys: %v\n", err) } key21, key22 := keys2[0], keys2[1] // authority 3 issues keys to user keys3, err := auth3.GenerateAttribKeys(gid, attribs3) if err != nil { t.Fatalf("Failed to generate attribute keys: %v\n", err) } key31, key32 := keys3[0], keys3[1] // try and generate key for an attribute that does not belong to the // authority (or does not exist) _, err = auth3.GenerateAttribKeys(gid, []string{"auth3:at3"}) assert.Error(t, err) // user tries to decrypt with different key combos ks1 := []*abe.MAABEKey{key11, key21, key31} // ok ks2 := []*abe.MAABEKey{key12, key22, key32} // ok ks3 := []*abe.MAABEKey{key11, key22} // not ok ks4 := []*abe.MAABEKey{key12, key21} // not ok ks5 := []*abe.MAABEKey{key31, key32} // ok // try to decrypt all messages msg1, err := maabe.Decrypt(ct, ks1) if err != nil { t.Fatalf("Error decrypting with keyset 1: %v\n", err) } assert.Equal(t, msg, msg1) msg2, err := maabe.Decrypt(ct, ks2) if err != nil { t.Fatalf("Error decrypting with keyset 2: %v\n", err) } assert.Equal(t, msg, msg2) _, err = maabe.Decrypt(ct, ks3) assert.Error(t, err) _, err = maabe.Decrypt(ct, ks4) assert.Error(t, err) msg5, err := maabe.Decrypt(ct, ks5) if err != nil { t.Fatalf("Error decrypting with keyset 5: %v\n", err) } assert.Equal(t, msg, msg5) // generate keys with a different GID gid2 := "gid2" // authority 1 issues keys to user foreignKeys, err := auth1.GenerateAttribKeys(gid2, []string{"auth1:at1"}) if err != nil { t.Fatalf("Failed to generate attribute key for %s: %v\n", "auth1:at1", err) } foreignKey11 := foreignKeys[0] // join two users who have sufficient attributes together, but not on their // own ks6 := []*abe.MAABEKey{foreignKey11, key21} // try and decrypt _, err = maabe.Decrypt(ct, ks6) assert.Error(t, err) // add a new attribute to some authority err = auth3.AddAttribute("auth3:at3") if err != nil { t.Fatalf("Error adding attribute: %v\n", err) } // now try to generate the key _, err = auth3.GenerateAttribKeys(gid, []string{"auth3:at3"}) if err != nil { t.Fatalf("Error generating key for new attribute: %v\n", err) } // regenerate a compromised key for some authority err = auth1.RegenerateKey("auth1:at2") if err != nil { t.Fatalf("Error regenerating key: %v\n", err) } // regenerate attrib key for that key and republish pubkey keysNew, err := auth1.GenerateAttribKeys(gid, []string{"auth1:at2"}) if err != nil { t.Fatalf("Error generating attrib key for regenerated key: %v\n", err) } key12New := keysNew[0] pks = []*abe.MAABEPubKey{auth1.Pk, auth2.Pk, auth3.Pk} // reencrypt msg ctNew, err := maabe.Encrypt(msg, msp, pks) if err != nil { t.Fatalf("Failed to encrypt with new keys") } ks7 := []*abe.MAABEKey{key12New, key22} // decrypt reencrypted msg msg7, err := maabe.Decrypt(ctNew, ks7) if err != nil { t.Fatalf("Failed to decrypt with regenerated keys: %v\n", err) } assert.Equal(t, msg, msg7) } ================================================ FILE: abe/policy.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package abe import ( "math/big" "strings" "fmt" "github.com/fentec-project/gofe/data" ) // MSP represents a monotone span program (MSP) describing a policy defining which // attributes are needed to decrypt the ciphertext. It includes a matrix // mat and a mapping from the rows of the mat to attributes. A MSP policy // allows decryption of an entity with a set of attributes A if an only if all the // rows of the matrix mapped to an element of A span the vector [1, 0,..., 0] (or // vector [1, 1,..., 1] depending on the use case). type MSP struct { P *big.Int Mat data.Matrix RowToAttrib []string } // BooleanToMSP takes as an input a boolean expression (without a NOT gate) as // a string where attributes are joined by AND and OR gates. It outputs a // msp structure representing the expression, i.e. a matrix whose rows // correspond to attributes used in the expression and with the property that a // boolean expression assigning 1 to some attributes is satisfied iff the // corresponding rows span a vector [1, 1,..., 1] or vector [1, 0,..., 0] // depending if parameter convertToOnes is set to true or false. Additionally a // vector is produced whose i-th entry indicates to which attribute the i-th row // corresponds. // Example: BooleanToMSP("attrib1 AND (attrib2 OR attrib3)", true) // The names of the attributes should not include "AND" or "OR" as // a substring and '(' or ')' as a character, otherwise the function // will not work properly. func BooleanToMSP(boolExp string, convertToOnes bool) (*MSP, error) { // by the Lewko-Waters algorithm we obtain a MSP struct with the property // that is the the boolean expression is satisfied if and only if the corresponding // rows of the msp matrix span the vector [1, 0,..., 0] vec := make(data.Vector, 1) vec[0] = big.NewInt(1) msp, _, err := booleanToMSPIterative(boolExp, vec, 1) if err != nil { return nil, err } // if convertToOnes is set to true convert the matrix to such a MSP // struct so that the boolean expression is satisfied iff the // corresponding rows span the vector [1, 1,..., 1] if convertToOnes { // create an invertible matrix that maps [1, 0,..., 0] to [1,1,...,1] invMat := make(data.Matrix, len(msp.Mat[0])) for i := 0; i < len(msp.Mat[0]); i++ { invMat[i] = make(data.Vector, len(msp.Mat[0])) for j := 0; j < len(msp.Mat[0]); j++ { if i == 0 || j == i { invMat[i][j] = big.NewInt(1) } else { invMat[i][j] = big.NewInt(0) } } } //change the msp matrix by multiplying with it the matrix invMat msp.Mat, err = msp.Mat.Mul(invMat) if err != nil { return nil, err } } return msp, nil } // booleanToMspIterative iteratively builds a msp structure by splitting the expression // into two parts separated by an AND or OR gate, generating a msp structure on each of // them, and joining both structures together. The structure is such the the boolean expression // assigning 1 to some attributes is satisfied iff the corresponding rows span a vector // [1, 0,..., 0]. The algorithm is known as Lewko-Waters algorithm, see Appendix G in // https://eprint.iacr.org/2010/351.pdf. func booleanToMSPIterative(boolExp string, vec data.Vector, c int) (*MSP, int, error) { boolExp = strings.TrimSpace(boolExp) numBrc := 0 var boolExp1 string var boolExp2 string var c1 int var cOut int var msp1 *MSP var msp2 *MSP var err error found := false // find the main AND or OR gate and iteratively call the function on // both the sub-expressions for i, e := range boolExp { if e == '(' { numBrc++ continue } if e == ')' { numBrc-- continue } if numBrc == 0 && i < len(boolExp)-3 && boolExp[i:i+3] == "AND" { boolExp1 = boolExp[:i] boolExp2 = boolExp[i+3:] vec1, vec2 := makeAndVecs(vec, c) msp1, c1, err = booleanToMSPIterative(boolExp1, vec1, c+1) if err != nil { return nil, 0, err } msp2, cOut, err = booleanToMSPIterative(boolExp2, vec2, c1) if err != nil { return nil, 0, err } found = true break } if numBrc == 0 && i < len(boolExp)-2 && boolExp[i:i+2] == "OR" { boolExp1 = boolExp[:i] boolExp2 = boolExp[i+2:] msp1, c1, err = booleanToMSPIterative(boolExp1, vec, c) if err != nil { return nil, 0, err } msp2, cOut, err = booleanToMSPIterative(boolExp2, vec, c1) if err != nil { return nil, 0, err } found = true break } } // If the AND or OR gate is not found then there are two options, // either the whole expression is in brackets, or the the expression // is only one attribute. It neither of both is true, then // an error is returned while converting the expression into an // attribute if !found { if boolExp[0] == '(' && boolExp[len(boolExp)-1] == ')' { boolExp = boolExp[1:(len(boolExp) - 1)] return booleanToMSPIterative(boolExp, vec, c) } if strings.Contains(boolExp, "(") || strings.Contains(boolExp, ")") { return nil, 0, fmt.Errorf("bad boolean expression or attributes contain ( or )") } mat := make(data.Matrix, 1) mat[0] = make(data.Vector, c) for i := 0; i < c; i++ { if i < len(vec) { mat[0][i] = new(big.Int).Set(vec[i]) } else { mat[0][i] = big.NewInt(0) } } rowToAttribS := make([]string, 1) rowToAttribS[0] = boolExp return &MSP{Mat: mat, RowToAttrib: rowToAttribS}, c, nil } // otherwise we join the two msp structures into one mat := make(data.Matrix, len(msp1.Mat)+len(msp2.Mat)) for i := 0; i < len(msp1.Mat); i++ { mat[i] = make(data.Vector, cOut) for j := 0; j < len(msp1.Mat[0]); j++ { mat[i][j] = msp1.Mat[i][j] } for j := len(msp1.Mat[0]); j < cOut; j++ { mat[i][j] = big.NewInt(0) } } for i := 0; i < len(msp2.Mat); i++ { mat[i+len(msp1.Mat)] = msp2.Mat[i] } rowToAttribS := append(msp1.RowToAttrib, msp2.RowToAttrib...) return &MSP{Mat: mat, RowToAttrib: rowToAttribS}, cOut, nil } // makeAndVecs is a helping structure that given a vector and and counter // creates two new vectors used whenever an AND gate is found in a iterative // step of BooleanToMsp func makeAndVecs(vec data.Vector, c int) (data.Vector, data.Vector) { vec1 := data.NewConstantVector(c+1, big.NewInt(0)) vec2 := data.NewConstantVector(c+1, big.NewInt(0)) for i := 0; i < len(vec); i++ { vec2[i].Set(vec[i]) } vec1[c] = big.NewInt(-1) vec2[c] = big.NewInt(1) return vec1, vec2 } ================================================ FILE: abe/policy_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package abe import ( "math/big" "testing" "github.com/fentec-project/gofe/data" "github.com/stretchr/testify/assert" ) func TestBooleanToMsp(t *testing.T) { // create as msp struct out of a boolean expression p := big.NewInt(7) msp, err := BooleanToMSP("1 AND (((6 OR 7) AND (8 OR 9)) OR ((2 AND 3) OR (4 AND 5)))", true) if err != nil { t.Fatalf("Error while processing a boolean expression: %v", err) } // check if having attributes 1, 7 and 9 satisfies the expression, i.e. entries 0, 2, 4 // of a msp matrix span vector [1, 1,..., 1], using Gaussian elimination v := make(data.Vector, len(msp.Mat[0])) for i := 0; i < len(v); i++ { v[i] = big.NewInt(1) } m := make(data.Matrix, 3) m[0] = msp.Mat[0] m[1] = msp.Mat[2] m[2] = msp.Mat[4] x, err := data.GaussianEliminationSolver(m.Transpose(), v, p) if err != nil { t.Fatalf("Error finding a vector: %v", err) } assert.NotNil(t, x) // check if an error is generated if the boolean expression is not in a correct form _, err = BooleanToMSP("1 AND ((6 OR 7) AND (8 OR 9)) OR ((2 AND 3) OR (4 AND 5)))", true) assert.Error(t, err) } ================================================ FILE: data/doc.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ // Package data defines basic mathematical structures used // throughout the library. package data ================================================ FILE: data/matrix.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package data import ( "fmt" "math/big" "github.com/fentec-project/bn256" "github.com/fentec-project/gofe/sample" ) // Matrix wraps a slice of Vector elements. It represents a row-major. // order matrix. // // The j-th element from the i-th vector of the matrix can be obtained // as m[i][j]. type Matrix []Vector // NewMatrix accepts a slice of Vector elements and // returns a new Matrix instance. // It returns error if not all the vectors have the same number of elements. func NewMatrix(vectors []Vector) (Matrix, error) { l := -1 newVectors := make([]Vector, len(vectors)) if len(vectors) > 0 { l = len(vectors[0]) } for i, v := range vectors { if len(v) != l { return nil, fmt.Errorf("all vectors should be of the same length") } newVectors[i] = NewVector(v) } return Matrix(newVectors), nil } // NewRandomMatrix returns a new Matrix instance // with random elements sampled by the provided sample.Sampler. // Returns an error in case of sampling failure. func NewRandomMatrix(rows, cols int, sampler sample.Sampler) (Matrix, error) { mat := make([]Vector, rows) for i := 0; i < rows; i++ { vec, err := NewRandomVector(cols, sampler) if err != nil { return nil, err } mat[i] = vec } return NewMatrix(mat) } // NewRandomDetMatrix returns a new Matrix instance // with random elements sampled by a pseudo-random // number generator. Elements are sampled from [0, max) and key // determines the pseudo-random generator. func NewRandomDetMatrix(rows, cols int, max *big.Int, key *[32]byte) (Matrix, error) { l := rows * cols v, err := NewRandomDetVector(l, max, key) if err != nil { return nil, err } mat := make([]Vector, rows) for i := 0; i < rows; i++ { mat[i] = NewVector(v[(i * cols):((i + 1) * cols)]) } return NewMatrix(mat) } // NewConstantMatrix returns a new Matrix instance // with all elements set to constant c. func NewConstantMatrix(rows, cols int, c *big.Int) Matrix { mat := make([]Vector, rows) for i := 0; i < rows; i++ { mat[i] = NewConstantVector(cols, c) } return mat } // Copy creates a new Matrix with the same values. func (m Matrix) Copy() Matrix { mat := make(Matrix, m.Rows()) for i := 0; i < m.Rows(); i++ { mat[i] = m[i].Copy() } return mat } // Rows returns the number of rows of matrix m. func (m Matrix) Rows() int { return len(m) } // Cols returns the number of columns of matrix m. func (m Matrix) Cols() int { if len(m) != 0 { return len(m[0]) } return 0 } // DimsMatch returns a bool indicating whether matrices // m and other have the same dimensions. func (m Matrix) DimsMatch(other Matrix) bool { return m.Rows() == other.Rows() && m.Cols() == other.Cols() } // GetCol returns i-th column of matrix m as a vector. // It returns error if i >= the number of m's columns. func (m Matrix) GetCol(i int) (Vector, error) { if i >= m.Cols() { return nil, fmt.Errorf("column index exceeds matrix dimensions") } column := make([]*big.Int, m.Rows()) for j := 0; j < m.Rows(); j++ { column[j] = m[j][i] } return NewVector(column), nil } // Transpose transposes matrix m and returns // the result in a new Matrix. func (m Matrix) Transpose() Matrix { transposed := make([]Vector, m.Cols()) for i := 0; i < m.Cols(); i++ { transposed[i], _ = m.GetCol(i) } mT, _ := NewMatrix(transposed) return mT } // CheckBound checks whether all matrix elements are strictly // smaller than the provided bound. // It returns error if at least one element is >= bound. func (m Matrix) CheckBound(bound *big.Int) error { for _, v := range m { err := v.CheckBound(bound) if err != nil { return err } } return nil } // CheckDims checks whether dimensions of matrix m match // the provided rows and cols arguments. func (m Matrix) CheckDims(rows, cols int) bool { return m.Rows() == rows && m.Cols() == cols } // Mod applies the element-wise modulo operation on matrix m. // The result is returned in a new Matrix. func (m Matrix) Mod(modulo *big.Int) Matrix { vectors := make([]Vector, m.Rows()) for i, v := range m { vectors[i] = v.Mod(modulo) } matrix, _ := NewMatrix(vectors) return matrix } // Apply applies an element-wise function f to matrix m. // The result is returned in a new Matrix. func (m Matrix) Apply(f func(*big.Int) *big.Int) Matrix { res := make(Matrix, len(m)) for i, vi := range m { res[i] = vi.Apply(f) } return res } // Dot calculates the dot product (inner product) of matrices m and other, // which we define as the sum of the dot product of rows of both matrices. // It returns an error if m and other have different dimensions. func (m Matrix) Dot(other Matrix) (*big.Int, error) { if !m.DimsMatch(other) { return nil, fmt.Errorf("matrices mismatch in dimensions") } r := new(big.Int) for i := 0; i < m.Rows(); i++ { prod, err := m[i].Dot(other[i]) if err != nil { return nil, err } r = r.Add(r, prod) } return r, nil } // Add adds matrices m and other. // The result is returned in a new Matrix. // Error is returned if m and other have different dimensions. func (m Matrix) Add(other Matrix) (Matrix, error) { if !m.DimsMatch(other) { return nil, fmt.Errorf("matrices mismatch in dimensions") } vectors := make([]Vector, m.Rows()) for i, v := range m { vectors[i] = v.Add(other[i]) } matrix, err := NewMatrix(vectors) if err != nil { return nil, err } return matrix, nil } // Sub adds matrices m and other. // The result is returned in a new Matrix. // Error is returned if m and other have different dimensions. func (m Matrix) Sub(other Matrix) (Matrix, error) { if !m.DimsMatch(other) { return nil, fmt.Errorf("matrices mismatch in dimensions") } vecs := make([]Vector, m.Rows()) for i, v := range m { vecs[i] = v.Sub(other[i]) } return NewMatrix(vecs) } // Mul multiplies matrices m and other. // The result is returned in a new Matrix. // Error is returned if m and other have different dimensions. func (m Matrix) Mul(other Matrix) (Matrix, error) { if m.Cols() != other.Rows() { return nil, fmt.Errorf("cannot multiply matrices") } prod := make([]Vector, m.Rows()) for i := 0; i < m.Rows(); i++ { prod[i] = make([]*big.Int, other.Cols()) for j := 0; j < other.Cols(); j++ { otherCol, _ := other.GetCol(j) prod[i][j], _ = m[i].Dot(otherCol) } } return NewMatrix(prod) } // MulScalar multiplies elements of matrix m by a scalar x. // The result is returned in a new Matrix. func (m Matrix) MulScalar(x *big.Int) Matrix { return m.Apply(func(i *big.Int) *big.Int { return new(big.Int).Mul(i, x) }) } // MulVec multiplies matrix m and vector v. // It returns the resulting vector. // Error is returned if the number of columns of m differs from the number // of elements of v. func (m Matrix) MulVec(v Vector) (Vector, error) { if m.Cols() != len(v) { return nil, fmt.Errorf("cannot multiply matrix by a vector") } res := make(Vector, m.Rows()) for i, row := range m { res[i], _ = row.Dot(v) } return res, nil } // MulXMatY calculates the function x^T * m * y, where x and y are // vectors. func (m Matrix) MulXMatY(x, y Vector) (*big.Int, error) { t, err := m.MulVec(y) if err != nil { return nil, err } v, err := t.Dot(x) if err != nil { return nil, err } return v, nil } // Minor returns a matrix obtained from m by removing row i and column j. // It returns an error if i >= number of rows of m, or if j >= number of // columns of m. func (m Matrix) Minor(i int, j int) (Matrix, error) { if i >= m.Rows() || j >= m.Cols() { return nil, fmt.Errorf("cannot obtain minor - out of bounds") } mat := make(Matrix, m.Rows()-1) for k := 0; k < m.Rows(); k++ { if k == i { continue } vec := make(Vector, 0, len(m[0])-1) vec = append(vec, m[k][:j]...) vec = append(vec, m[k][j+1:]...) if k < i { mat[k] = vec } else { mat[k-1] = vec } } return NewMatrix(mat) } // Determinant returns the determinant of matrix m. // It returns an error if the determinant does not exist. func (m Matrix) Determinant() (*big.Int, error) { if m.Rows() == 1 { return new(big.Int).Set(m[0][0]), nil } det := big.NewInt(0) sign := big.NewInt(1) for i := 0; i < m.Rows(); i++ { minor, err := m.Minor(0, i) if err != nil { return nil, err } value, err := minor.Determinant() if err != nil { return nil, err } value.Mul(value, m[0][i]) value.Mul(value, sign) sign.Neg(sign) det.Add(det, value) } return det, nil } // InverseMod returns the inverse matrix of m in the group Z_p. // Note that as we consider only matrix with integers, // the inverse exists only in Z_p. // // It returns an error in case matrix is not invertible. func (m Matrix) InverseMod(p *big.Int) (Matrix, error) { mat := make(Matrix, m.Rows()) det, err := m.Determinant() if err != nil { return nil, err } det.Mod(det, p) if det.Cmp(big.NewInt(0)) == 0 { return nil, fmt.Errorf("matrix non-invertable") } invDet := new(big.Int).ModInverse(det, p) if m.Rows() == 1 { mat[0] = Vector{invDet} return mat, nil } sign := new(big.Int) minusOne := big.NewInt(-1) for i := 0; i < m.Rows(); i++ { row := make(Vector, m.Cols()) for j := 0; j < m.Cols(); j++ { minor, err := m.Minor(i, j) if err != nil { return nil, err } value, err := minor.Determinant() if err != nil { return nil, err } value.Mod(value, p) sign.Exp(minusOne, big.NewInt(int64(i+j)), nil) value.Mul(value, sign) value.Mul(value, invDet) value.Mod(value, p) row[j] = value } mat[i] = row } co, err := NewMatrix(mat) if err != nil { return nil, err } return co.Transpose(), nil } // MulG1 calculates m * [bn256.G1] and returns the // result in a new MatrixG1 instance. func (m Matrix) MulG1() MatrixG1 { prod := make(MatrixG1, len(m)) for i := range prod { prod[i] = m[i].MulG1() } return prod } // MulG2 calculates m * [bn256.G1] and returns the // result in a new MatrixG2 instance. func (m Matrix) MulG2() MatrixG2 { prod := make(MatrixG2, len(m)) for i := range prod { prod[i] = m[i].MulG2() } return prod } // MatMulMatG1 multiplies m and other in the sense that // if other is t * [bn256.G1] for some matrix t, then the // function returns m * t * [bn256.G1] where m * t is a // matrix multiplication. func (m Matrix) MatMulMatG1(other MatrixG1) (MatrixG1, error) { if m.Cols() != other.Rows() { return nil, fmt.Errorf("cannot multiply matrices") } prod := make(MatrixG1, m.Rows()) for i := 0; i < m.Rows(); i++ { prod[i] = make([]*bn256.G1, other.Cols()) for j := 0; j < other.Cols(); j++ { prod[i][j] = new(bn256.G1).ScalarBaseMult(big.NewInt(0)) for k := 0; k < m.Cols(); k++ { mik := new(big.Int).Set(m[i][k]) okj := new(bn256.G1).Set(other[k][j]) if m[i][k].Sign() == -1 { okj.Neg(okj) mik.Neg(mik) } tmp := new(bn256.G1).ScalarMult(okj, mik) prod[i][j].Add(tmp, prod[i][j]) } } } return prod, nil } // MatMulMatG2 multiplies m and other in the sense that // if other is t * [bn256.G2] for some matrix t, then the // function returns m * t * [bn256.G2] where m * t is a // matrix multiplication. func (m Matrix) MatMulMatG2(other MatrixG2) (MatrixG2, error) { if m.Cols() != other.Rows() { return nil, fmt.Errorf("cannot multiply matrices") } prod := make(MatrixG2, m.Rows()) for i := 0; i < m.Rows(); i++ { prod[i] = make([]*bn256.G2, other.Cols()) for j := 0; j < other.Cols(); j++ { prod[i][j] = new(bn256.G2).ScalarBaseMult(big.NewInt(0)) for k := 0; k < m.Cols(); k++ { mik := new(big.Int).Set(m[i][k]) okj := new(bn256.G2).Set(other[k][j]) if m[i][k].Sign() == -1 { okj.Neg(okj) mik.Neg(mik) } tmp := new(bn256.G2).ScalarMult(okj, mik) prod[i][j].Add(tmp, prod[i][j]) } } } return prod, nil } // MatMulVecG2 multiplies m and other in the sense that // if other is t * [bn256.G2] for some vector t, then the // function returns m * t * [bn256.G2] where m * t is a // matrix-vector multiplication. func (m Matrix) MatMulVecG2(other VectorG2) (VectorG2, error) { if m.Cols() != len(other) { return nil, fmt.Errorf("dimensions don't fit") } prod := make(VectorG2, m.Rows()) for j := 0; j < m.Rows(); j++ { prod[j] = new(bn256.G2).ScalarBaseMult(big.NewInt(0)) for k := 0; k < m.Cols(); k++ { mjk := new(big.Int).Set(m[j][k]) ok := new(bn256.G2).Set(other[k]) if m[j][k].Sign() == -1 { ok.Neg(ok) mjk.Neg(mjk) } tmp := new(bn256.G2).ScalarMult(ok, mjk) prod[j].Add(tmp, prod[j]) } } return prod, nil } // GaussianElimination uses Gaussian elimination to transform a matrix // into an equivalent upper triangular form func (m Matrix) GaussianElimination(p *big.Int) (Matrix, error) { if m.Rows() == 0 || m.Cols() == 0 { return nil, fmt.Errorf("the matrix should not be empty") } // we copy matrix m into res and v into u res := make(Matrix, m.Rows()) for i := 0; i < m.Rows(); i++ { res[i] = make(Vector, m.Cols()) for j := 0; j < m.Cols(); j++ { res[i][j] = new(big.Int).Set(m[i][j]) } } // res and u are transformed to be in the upper triangular form h, k := 0, 0 for h < m.Rows() && k < res.Cols() { zero := true for i := h; i < m.Rows(); i++ { if res[i][k].Sign() != 0 { res[h], res[i] = res[i], res[h] zero = false break } } if zero { k++ continue } mHKInv := new(big.Int).ModInverse(res[h][k], p) for i := h + 1; i < m.Rows(); i++ { f := new(big.Int).Mul(mHKInv, res[i][k]) res[i][k] = big.NewInt(0) for j := k + 1; j < res.Cols(); j++ { res[i][j].Sub(res[i][j], new(big.Int).Mul(f, res[h][j])) res[i][j].Mod(res[i][j], p) } } k++ h++ } return res, nil } // InverseModGauss returns the inverse matrix of m in the group Z_p. // The algorithm uses Gaussian elimination. It returns the determinant // as well. In case the matrix is not invertible it returns an error. func (m Matrix) InverseModGauss(p *big.Int) (Matrix, *big.Int, error) { if m.Rows() == 0 || m.Cols() == 0 { return nil, nil, fmt.Errorf("the matrix should not be empty") } if m.Rows() != m.Cols() { return nil, nil, fmt.Errorf("the number of rows must equal the number of columns") } // we copy matrix m into matExt and extend it with identity matExt := make(Matrix, m.Rows()) for i := 0; i < m.Rows(); i++ { matExt[i] = make(Vector, m.Cols()*2) for j := 0; j < m.Cols(); j++ { matExt[i][j] = new(big.Int).Set(m[i][j]) } for j := m.Cols(); j < 2*m.Cols(); j++ { if i+m.Cols() == j { matExt[i][j] = big.NewInt(1) } else { matExt[i][j] = big.NewInt(0) } } } triang, err := matExt.GaussianElimination(p) if err != nil { return nil, nil, err } // check if the inverse can be computed det := big.NewInt(1) for i := 0; i < matExt.Rows(); i++ { det.Mul(det, triang[i][i]) det.Mod(det, p) } if det.Sign() == 0 { return nil, det, fmt.Errorf("matrix non-invertable") } // use the upper triangular form to obtain the solution matInv := make(Matrix, m.Rows()) for k := 0; k < m.Rows(); k++ { matInv[k] = make(Vector, m.Cols()) for i := m.Rows() - 1; i >= 0; i-- { for j := m.Rows() - 1; j >= 0; j-- { if matInv[k][j] == nil { tmpSum, _ := triang[i][j+1 : m.Cols()].Dot(matInv[k][j+1:]) matInv[k][j] = new(big.Int).Sub(triang[i][m.Cols()+k], tmpSum) mHKInv := new(big.Int).ModInverse(triang[i][j], p) matInv[k][j].Mul(matInv[k][j], mHKInv) matInv[k][j].Mod(matInv[k][j], p) break } } } } return matInv.Transpose(), det, nil } // DeterminantGauss returns the determinant of matrix m using Gaussian // elimination. It returns an error if the determinant does not exist. func (m Matrix) DeterminantGauss(p *big.Int) (*big.Int, error) { if m.Rows() != m.Cols() { return nil, fmt.Errorf("number of rows must equal number of columns") } triang, err := m.GaussianElimination(p) if err != nil { return nil, err } ret := big.NewInt(1) for i := 0; i < m.Cols(); i++ { ret.Mul(ret, triang[i][i]) ret.Mod(ret, p) } return ret, nil } // GaussianEliminationSolver solves a vector equation mat * x = v and finds vector x, // using Gaussian elimination. Arithmetic operations are considered to be over // Z_p, where p should be a prime number. If such x does not exist, then the // function returns an error. func GaussianEliminationSolver(mat Matrix, v Vector, p *big.Int) (Vector, error) { if mat.Rows() == 0 || mat.Cols() == 0 { return nil, fmt.Errorf("the matrix should not be empty") } if mat.Rows() != len(v) { return nil, fmt.Errorf(fmt.Sprintf("dimensions should match: "+ "rows of the matrix %d, length of the vector %d", mat.Rows(), len(v))) } // we copy matrix mat into m and v into u cpMat := make([]Vector, mat.Rows()) u := make(Vector, mat.Rows()) for i := 0; i < mat.Rows(); i++ { cpMat[i] = make(Vector, mat.Cols()) for j := 0; j < mat.Cols(); j++ { cpMat[i][j] = new(big.Int).Set(mat[i][j]) } u[i] = new(big.Int).Set(v[i]) } m, _ := NewMatrix(cpMat) // error is impossible to happen // m and u are transformed to be in the upper triangular form ret := make(Vector, mat.Cols()) h, k := 0, 0 for h < mat.Rows() && k < mat.Cols() { zero := true for i := h; i < mat.Rows(); i++ { if m[i][k].Sign() != 0 { m[h], m[i] = m[i], m[h] u[h], u[i] = u[i], u[h] zero = false break } } if zero { ret[k] = big.NewInt(0) k++ continue } mHKInv := new(big.Int).ModInverse(m[h][k], p) for i := h + 1; i < mat.Rows(); i++ { f := new(big.Int).Mul(mHKInv, m[i][k]) m[i][k] = big.NewInt(0) for j := k + 1; j < mat.Cols(); j++ { m[i][j].Sub(m[i][j], new(big.Int).Mul(f, m[h][j])) m[i][j].Mod(m[i][j], p) } u[i].Sub(u[i], new(big.Int).Mul(f, u[h])) u[i].Mod(u[i], p) } k++ h++ } for i := h; i < mat.Rows(); i++ { if u[i].Sign() != 0 { return nil, fmt.Errorf("no solution") } } for j := k; j < mat.Cols(); j++ { ret[j] = big.NewInt(0) } // use the upper triangular form to obtain the solution for i := h - 1; i >= 0; i-- { for j := k - 1; j >= 0; j-- { if ret[j] == nil { tmpSum, _ := m[i][j+1:].Dot(ret[j+1:]) ret[j] = new(big.Int).Sub(u[i], tmpSum) mHKInv := new(big.Int).ModInverse(m[i][j], p) ret[j].Mul(ret[j], mHKInv) ret[j].Mod(ret[j], p) break } } } return ret, nil } // Tensor creates a tensor product of matrices m and other. // The result is returned in a new Matrix. func (m Matrix) Tensor(other Matrix) Matrix { prod := make(Matrix, m.Rows()*other.Rows()) for i := 0; i < prod.Rows(); i++ { prod[i] = make(Vector, m.Cols()*other.Cols()) for j := 0; j < len(prod[i]); j++ { prod[i][j] = new(big.Int).Mul(m[i/other.Rows()][j/other.Cols()], other[i%other.Rows()][j%other.Cols()]) } } return prod } // ToVec creates a vector whose entries are entries of m // ordered as m_11, m_12,..., m_21, m_22,..., m_kl. func (m Matrix) ToVec() Vector { res := make(Vector, m.Rows()*m.Cols()) for i := 0; i < m.Rows(); i++ { for j := 0; j < m.Cols(); j++ { res[m.Cols()*i+j] = new(big.Int).Set(m[i][j]) } } return res } // JoinCols joins matrices m and other in a new Matrix // with columns from both matrices. func (m Matrix) JoinCols(other Matrix) (Matrix, error) { if m.Rows() != other.Rows() { return nil, fmt.Errorf("dimensions do not fit") } res := make(Matrix, m.Rows()) for i := 0; i < m.Rows(); i++ { res[i] = make(Vector, m.Cols()+other.Cols()) for j := 0; j < m.Cols()+other.Cols(); j++ { if j < m.Cols() { res[i][j] = new(big.Int).Set(m[i][j]) } else { res[i][j] = new(big.Int).Set(other[i][j-m.Cols()]) } } } return res, nil } // JoinRows joins matrices m and other in a new Matrix // with rows from both matrices. func (m Matrix) JoinRows(other Matrix) (Matrix, error) { if m.Cols() != other.Cols() { return nil, fmt.Errorf("dimensions do not fit") } res := make(Matrix, m.Rows()+other.Rows()) for i := 0; i < res.Rows(); i++ { res[i] = make(Vector, m.Cols()) for j := 0; j < m.Cols(); j++ { if i < m.Rows() { res[i][j] = new(big.Int).Set(m[i][j]) } else { res[i][j] = new(big.Int).Set(other[i-m.Rows()][j]) } } } return res, nil } // Identity returns the identity matrix with given dimensions. func Identity(rows, cols int) Matrix { res := NewConstantMatrix(rows, cols, big.NewInt(0)) for i := 0; i < rows && i < cols; i++ { res[i][i].SetInt64(1) } return res } ================================================ FILE: data/matrix_bn256.go ================================================ package data import ( "math/big" "github.com/fentec-project/bn256" ) // MatrixG1 wraps a slice of VectorG1 elements. It represents a row-major. // order matrix. // // The j-th element from the i-th vector of the matrix can be obtained // as m[i][j]. type MatrixG1 []VectorG1 // Rows returns the number of rows of matrixG1 m. func (m MatrixG1) Rows() int { return len(m) } // Cols returns the number of columns of matrixG1 m. func (m MatrixG1) Cols() int { if len(m) != 0 { return len(m[0]) } return 0 } // Add sums matrices m and other componentwise. // It returns the result in a new MatrixG1 instance. func (m MatrixG1) Add(other MatrixG1) MatrixG1 { sum := make(MatrixG1, len(m)) for i := range sum { sum[i] = m[i].Add(other[i]) } return sum } // MulScalar multiplies matrix m by a scalar s. // It returns the result in a new MatrixG1 instance. func (m MatrixG1) MulScalar(s *big.Int) MatrixG1 { out := make(MatrixG1, m.Rows()) for i := range out { out[i] = m[i].MulScalar(s) } return out } // MulVector multiplies matrix m by a vector v, i.e if // m is t * [bn256.G1] for some matrix t, then the result // is (t * v) [bn256.G1] func (m MatrixG1) MulVector(v Vector) VectorG1 { out := make(VectorG1, m.Rows()) for i := range out { out[i] = new(bn256.G1).ScalarBaseMult(big.NewInt(0)) for k := 0; k < m.Cols(); k++ { mik := new(bn256.G1).Set(m[i][k]) vk := new(big.Int).Set(v[k]) if v[k].Sign() == -1 { vk.Neg(vk) mik.Neg(mik) } tmp := new(bn256.G1).ScalarMult(mik, vk) out[i].Add(tmp, out[i]) } } return out } // MatrixG2 wraps a slice of VectorG2 elements. It represents a row-major. // order matrix. // // The j-th element from the i-th vector of the matrix can be obtained // as m[i][j]. type MatrixG2 []VectorG2 // Rows returns the number of rows of matrixG2 m. func (m MatrixG2) Rows() int { return len(m) } // Cols returns the number of columns of matrixG2 m. func (m MatrixG2) Cols() int { if len(m) != 0 { return len(m[0]) } return 0 } // MulScalar multiplies matrix m by a scalar s func (m MatrixG2) MulScalar(s *big.Int) MatrixG2 { out := make(MatrixG2, m.Rows()) for i := range out { out[i] = m[i].MulScalar(s) } return out } // MulVector multiplies matrix m by a vector v, i.e if // m is t * [bn256.G2] for some matrix t, then the result // is (t * v) [bn256.G2] func (m MatrixG2) MulVector(v Vector) VectorG2 { out := make(VectorG2, m.Rows()) for i := range out { out[i] = new(bn256.G2).ScalarBaseMult(big.NewInt(0)) for k := 0; k < m.Cols(); k++ { mik := new(bn256.G2).Set(m[i][k]) vk := new(big.Int).Set(v[k]) if v[k].Sign() == -1 { vk.Neg(vk) mik.Neg(mik) } tmp := new(bn256.G2).ScalarMult(mik, vk) out[i].Add(tmp, out[i]) } } return out } ================================================ FILE: data/matrix_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package data import ( "math/big" "testing" "github.com/fentec-project/gofe/sample" "github.com/stretchr/testify/assert" ) func TestMatrix(t *testing.T) { rows, cols := 5, 3 bound := new(big.Int).Exp(big.NewInt(2), big.NewInt(20), big.NewInt(0)) sampler := sample.NewUniform(bound) x, err := NewRandomMatrix(rows, cols, sampler) if err != nil { t.Fatalf("Error during random generation: %v", err) } y, err := NewRandomMatrix(rows, cols, sampler) if err != nil { t.Fatalf("Error during random generation: %v", err) } add, err := x.Add(y) if err != nil { t.Fatalf("Error during matrix addition: %v", err) } modulo := big.NewInt(int64(104729)) mod := x.Mod(modulo) for i := 0; i < rows; i++ { for j := 0; j < cols; j++ { assert.Equal(t, new(big.Int).Add(x[i][j], y[i][j]), add[i][j], "coordinates should sum correctly") assert.Equal(t, new(big.Int).Mod(x[i][j], modulo), mod[i][j], "coordinates should mod correctly") } } sampler = sample.NewUniform(big.NewInt(256)) var key [32]byte for i := range key { r, _ := sampler.Sample() key[i] = byte(r.Int64()) } _, err = NewRandomDetMatrix(100, 100, big.NewInt(5), &key) assert.Equal(t, err, nil) } func TestMatrix_Rows(t *testing.T) { m, _ := NewRandomMatrix(2, 3, sample.NewUniform(big.NewInt(10))) assert.Equal(t, 2, m.Rows()) } func TestMatrix_Cols(t *testing.T) { m, _ := NewRandomMatrix(2, 3, sample.NewUniform(big.NewInt(10))) assert.Equal(t, 3, m.Cols()) } func TestMatrix_Empty(t *testing.T) { var m Matrix assert.Equal(t, 0, m.Rows()) assert.Equal(t, 0, m.Cols()) } func TestMatrix_DimsMatch(t *testing.T) { sampler := sample.NewUniform(big.NewInt(10)) m1, _ := NewRandomMatrix(2, 3, sampler) m2, _ := NewRandomMatrix(2, 3, sampler) m3, _ := NewRandomMatrix(2, 4, sampler) m4, _ := NewRandomMatrix(3, 3, sampler) assert.True(t, m1.DimsMatch(m2)) assert.False(t, m1.DimsMatch(m3)) assert.False(t, m1.DimsMatch(m4)) } func TestMatrix_CheckDims(t *testing.T) { sampler := sample.NewUniform(big.NewInt(10)) m, _ := NewRandomMatrix(2, 2, sampler) assert.True(t, m.CheckDims(2, 2)) assert.False(t, m.CheckDims(2, 3)) assert.False(t, m.CheckDims(3, 2)) assert.False(t, m.CheckDims(3, 3)) } func TestMatrix_Dot(t *testing.T) { m1 := Matrix{ Vector{big.NewInt(1), big.NewInt(2)}, Vector{big.NewInt(3), big.NewInt(4)}, } m2 := Matrix{ Vector{big.NewInt(4), big.NewInt(3)}, Vector{big.NewInt(2), big.NewInt(1)}, } mismatched := Matrix{ Vector{big.NewInt(1), big.NewInt(2)}, } dot, _ := m1.Dot(m2) _, err := m1.Dot(mismatched) assert.Equal(t, big.NewInt(20), dot, "dot product of matrices does not work correctly") assert.Error(t, err, "expected an error to because of dimension mismatch") } func TestMatrix_MulScalar(t *testing.T) { one := big.NewInt(1) two := big.NewInt(2) m := Matrix{ Vector{one, one, one}, Vector{one, one, one}, } mTimesTwo := Matrix{ Vector{two, two, two}, Vector{two, two, two}, } assert.Equal(t, m.MulScalar(two), mTimesTwo) } func TestMatrix_MulVec(t *testing.T) { m := Matrix{ Vector{big.NewInt(1), big.NewInt(2), big.NewInt(3)}, Vector{big.NewInt(4), big.NewInt(5), big.NewInt(6)}, } v := Vector{big.NewInt(2), big.NewInt(2), big.NewInt(2)} vMismatched := Vector{big.NewInt(1)} mvExpected := Vector{big.NewInt(12), big.NewInt(30)} mv, _ := m.MulVec(v) _, err := m.MulVec(vMismatched) assert.Equal(t, mvExpected, mv, "product of matrix and vector does not work correctly") assert.Error(t, err, "expected an error to because of dimension mismatch") } func TestMatrix_Mul(t *testing.T) { m1 := Matrix{ Vector{big.NewInt(1), big.NewInt(2), big.NewInt(3)}, Vector{big.NewInt(4), big.NewInt(5), big.NewInt(6)}, } m2 := Matrix{ Vector{big.NewInt(1), big.NewInt(2)}, Vector{big.NewInt(3), big.NewInt(4)}, Vector{big.NewInt(5), big.NewInt(6)}, } mismatched := Matrix{Vector{big.NewInt(1)}} prodExpected := Matrix{ Vector{big.NewInt(22), big.NewInt(28)}, Vector{big.NewInt(49), big.NewInt(64)}, } prod, _ := m1.Mul(m2) _, err := m1.Mul(mismatched) assert.Equal(t, prodExpected, prod, "product of matrices does not work correctly") assert.Error(t, err, "expected an error to because of dimension mismatch") } func TestMatrix_Determinant(t *testing.T) { m := Matrix{ Vector{big.NewInt(1), big.NewInt(1), big.NewInt(1)}, Vector{big.NewInt(4), big.NewInt(5), big.NewInt(6)}, Vector{big.NewInt(4), big.NewInt(4), big.NewInt(3)}, } p := big.NewInt(7) det1, err := m.Determinant() if err != nil { t.Fatalf("Error during computation of determinant: %v", err) } det1.Mod(det1, p) det2, err := m.DeterminantGauss(p) if err != nil { t.Fatalf("Error during computation of determinant: %v", err) } assert.Equal(t, det1, det2, "computed determinants are not equal") } func TestMatrix_InverseMod(t *testing.T) { m := Matrix{ Vector{big.NewInt(1), big.NewInt(1), big.NewInt(1)}, Vector{big.NewInt(4), big.NewInt(5), big.NewInt(6)}, Vector{big.NewInt(4), big.NewInt(4), big.NewInt(3)}, } p := big.NewInt(7) mInv1, err := m.InverseMod(p) if err != nil { t.Fatalf("Error during computation of inverse: %v", err) } mInv2, _, err := m.InverseModGauss(p) if err != nil { t.Fatalf("Error during computation of inverse: %v", err) } for i := 0; i < m.Rows(); i++ { for j := 0; j < m.Cols(); j++ { assert.Equal(t, mInv1[i][j].Cmp(mInv2[i][j]), 0, "computed inverses are not equal") } } } func TestMatrix_GaussianElimintaion(t *testing.T) { // create instances mat, xTest and v for which mat * xTest = v // as a matrix vector multiplication over Z_p p := big.NewInt(17) sampler := sample.NewUniform(p) mat, err := NewRandomMatrix(100, 50, sampler) if err != nil { t.Fatalf("Error during matrix generation: %v", err) } xTest, err := NewRandomVector(50, sampler) if err != nil { t.Fatalf("Error during vector generation: %v", err) } v, err := mat.MulVec(xTest) if err != nil { t.Fatalf("Error in generating a test vector: %v", err) } v = v.Mod(p) // test the Gaussian elimination algorithm that given v and mat // finds x such that mat * x = v x, err := GaussianEliminationSolver(mat, v, p) if err != nil { t.Fatalf("Error in Gaussian elimination: %v", err) } // test if the obtained x is correct vCheck, err := mat.MulVec(x) if err != nil { t.Fatalf("Error obtainig a check value: %v", err) } vCheck = vCheck.Mod(p) assert.Equal(t, v, vCheck) // test if errors are returned if the inputs have a wrong form vWrong, err := NewRandomVector(101, sampler) if err != nil { t.Fatalf("Error during vector generation: %v", err) } _, err = GaussianEliminationSolver(mat, vWrong, p) assert.Error(t, err) matWrong := make(Matrix, 0) _, err = GaussianEliminationSolver(matWrong, v, p) assert.Error(t, err) } func TestMatrix_Tensor(t *testing.T) { m1 := Matrix{ Vector{big.NewInt(1), big.NewInt(2)}, Vector{big.NewInt(3), big.NewInt(4)}, } m2 := Matrix{ Vector{big.NewInt(1), big.NewInt(2)}, } prodExpected := Matrix{ Vector{big.NewInt(1), big.NewInt(2), big.NewInt(2), big.NewInt(4)}, Vector{big.NewInt(3), big.NewInt(6), big.NewInt(4), big.NewInt(8)}, } prod := m1.Tensor(m2) assert.Equal(t, prodExpected, prod, "tensor product of matrices does not work correctly") } ================================================ FILE: data/vector.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package data import ( "fmt" "math/big" "github.com/fentec-project/bn256" "github.com/fentec-project/gofe/sample" "golang.org/x/crypto/salsa20" ) // Vector wraps a slice of *big.Int elements. type Vector []*big.Int // NewVector returns a new Vector instance. func NewVector(coordinates []*big.Int) Vector { return Vector(coordinates) } // NewRandomVector returns a new Vector instance // with random elements sampled by the provided sample.Sampler. // Returns an error in case of sampling failure. func NewRandomVector(len int, sampler sample.Sampler) (Vector, error) { vec := make([]*big.Int, len) var err error for i := 0; i < len; i++ { vec[i], err = sampler.Sample() if err != nil { return nil, err } } return NewVector(vec), nil } // NewRandomDetVector returns a new Vector instance // with (deterministic) random elements sampled by a pseudo-random // number generator. Elements are sampled from [0, max) and key // determines the pseudo-random generator. func NewRandomDetVector(len int, max *big.Int, key *[32]byte) (Vector, error) { if max.Cmp(big.NewInt(2)) < 0 { return nil, fmt.Errorf("upper bound on samples should be at least 2") } maxBits := new(big.Int).Sub(max, big.NewInt(1)).BitLen() maxBytes := (maxBits + 7) / 8 over := uint((8 * maxBytes) - maxBits) lTimesMaxBytes := len * maxBytes nonce := make([]byte, 8) // nonce is initialized to zeros ret := make([]*big.Int, len) for i := 3; true; i++ { in := make([]byte, i*lTimesMaxBytes) // input is initialized to zeros out := make([]byte, i*lTimesMaxBytes) salsa20.XORKeyStream(out, in, nonce, key) j := 0 k := 0 for j < (i * lTimesMaxBytes) { out[j] = out[j] >> over ret[k] = new(big.Int).SetBytes(out[j:(j + maxBytes)]) if ret[k].Cmp(max) < 0 { k++ } if k == len { break } j += maxBytes } if k == len { break } } return NewVector(ret), nil } // NewConstantVector returns a new Vector instance // with all elements set to constant c. func NewConstantVector(len int, c *big.Int) Vector { vec := make([]*big.Int, len) for i := 0; i < len; i++ { vec[i] = new(big.Int).Set(c) } return vec } // Copy creates a new vector with the same values // of the entries. func (v Vector) Copy() Vector { newVec := make(Vector, len(v)) for i, c := range v { newVec[i] = new(big.Int).Set(c) } return newVec } // MulScalar multiplies vector v by a given scalar x. // The result is returned in a new Vector. func (v Vector) MulScalar(x *big.Int) Vector { res := make(Vector, len(v)) for i, vi := range v { res[i] = new(big.Int).Mul(x, vi) } return res } // Mod performs modulo operation on vector's elements. // The result is returned in a new Vector. func (v Vector) Mod(modulo *big.Int) Vector { newCoords := make([]*big.Int, len(v)) for i, c := range v { newCoords[i] = new(big.Int).Mod(c, modulo) } return NewVector(newCoords) } // CheckBound checks whether the absolute values of all vector elements // are strictly smaller than the provided bound. // It returns error if at least one element's absolute value is >= bound. func (v Vector) CheckBound(bound *big.Int) error { abs := new(big.Int) for _, c := range v { abs.Abs(c) if abs.Cmp(bound) > 0 { return fmt.Errorf("all coordinates of a vector should not be greater than bound") } } return nil } // Apply applies an element-wise function f to vector v. // The result is returned in a new Vector. func (v Vector) Apply(f func(*big.Int) *big.Int) Vector { res := make(Vector, len(v)) for i, vi := range v { res[i] = f(vi) } return res } // Neg returns -v for given vector v. // The result is returned in a new Vector. func (v Vector) Neg() Vector { neg := make([]*big.Int, len(v)) for i, c := range v { neg[i] = new(big.Int).Neg(c) } return neg } // Add adds vectors v and other. // The result is returned in a new Vector. func (v Vector) Add(other Vector) Vector { sum := make([]*big.Int, len(v)) for i, c := range v { sum[i] = new(big.Int).Add(c, other[i]) } return NewVector(sum) } // Sub subtracts vectors v and other. // The result is returned in a new Vector. func (v Vector) Sub(other Vector) Vector { sub := make([]*big.Int, len(v)) for i, c := range v { sub[i] = new(big.Int).Sub(c, other[i]) } return sub } // Dot calculates the dot product (inner product) of vectors v and other. // It returns an error if vectors have different numbers of elements. func (v Vector) Dot(other Vector) (*big.Int, error) { prod := big.NewInt(0) if len(v) != len(other) { return nil, fmt.Errorf("vectors should be of same length") } for i, c := range v { prod = prod.Add(prod, new(big.Int).Mul(c, other[i])) } return prod, nil } // MulAsPolyInRing multiplies vectors v and other as polynomials // in the ring of polynomials R = Z[x]/((x^n)+1), where n is length of // the vectors. Note that the input vector [1, 2, 3] represents a // polynomial Z[x] = x²+2x+3. // It returns a new polynomial with degree <= n-1. // // If vectors differ in size, error is returned. func (v Vector) MulAsPolyInRing(other Vector) (Vector, error) { if len(v) != len(other) { return nil, fmt.Errorf("vectors must have the same length") } n := len(v) // Result will be a polynomial with the degree <= n-1 prod := new(big.Int) res := make(Vector, n) // Over all degrees, beginning at lowest degree for i := 0; i < n; i++ { res[i] = big.NewInt(0) // Handle products with degrees < n for j := 0; j <= i; j++ { prod.Mul(v[i-j], other[j]) // Multiply coefficients res[i].Add(res[i], prod) } // Handle products with degrees >= n for j := i + 1; j < n; j++ { prod.Mul(v[n+i-j], other[j]) // Multiply coefficients prod.Neg(prod) // Negate, because x^n = -1 res[i].Add(res[i], prod) } } return res, nil } // MulG1 calculates bn256.G1 * v (also g1^v in multiplicative notation) // and returns the result (v[0] * bn256.G1, ... , v[n-1] * bn256.G1) in a // VectorG1 instance. func (v Vector) MulG1() VectorG1 { prod := make(VectorG1, len(v)) for i := range prod { vi := new(big.Int).Abs(v[i]) prod[i] = new(bn256.G1).ScalarBaseMult(vi) if v[i].Sign() == -1 { prod[i].Neg(prod[i]) } } return prod } // MulVecG1 calculates g1 * v (also g1^v in multiplicative notation) // and returns the result (v[0] * g1[0], ... , v[n-1] * g1[n-1]) in a // VectorG1 instance. func (v Vector) MulVecG1(g1 VectorG1) VectorG1 { zero := big.NewInt(0) prod := make(VectorG1, len(v)) for i := range prod { vi := new(big.Int).Set(v[i]) g1i := new(bn256.G1).Set(g1[i]) if vi.Cmp(zero) == -1 { g1i.Neg(g1i) vi.Neg(vi) } prod[i] = new(bn256.G1).ScalarMult(g1i, vi) } return prod } // MulG2 calculates bn256.G2 * v (also g2^v in multiplicative notation) // and returns the result (v[0] * bn256.G2, ... , v[n-1] * bn256.G2) in a // VectorG2 instance. func (v Vector) MulG2() VectorG2 { prod := make(VectorG2, len(v)) for i := range prod { vi := new(big.Int).Abs(v[i]) prod[i] = new(bn256.G2).ScalarBaseMult(vi) if v[i].Sign() == -1 { prod[i].Neg(prod[i]) } } return prod } // MulVecG2 calculates g2 * v (also g2^v in multiplicative notation) // and returns the result (v[0] * g2[0], ... , v[n-1] * g2[n-1]) in a // VectorG2 instance. func (v Vector) MulVecG2(g2 VectorG2) VectorG2 { zero := big.NewInt(0) prod := make(VectorG2, len(v)) for i := range prod { vi := new(big.Int).Set(v[i]) g2i := new(bn256.G2).Set(g2[i]) if vi.Cmp(zero) == -1 { g2i.Neg(g2i) vi.Neg(vi) } prod[i] = new(bn256.G2).ScalarMult(g2i, vi) } return prod } // String produces a string representation of a vector. func (v Vector) String() string { vStr := "" for _, yi := range v { vStr = vStr + " " + yi.String() } return vStr } // Tensor creates a tensor product of vectors v and other. // The result is returned in a new Vector. func (v Vector) Tensor(other Vector) Vector { prod := make(Vector, len(v)*len(other)) for i := 0; i < len(prod); i++ { prod[i] = new(big.Int).Mul(v[i/len(other)], other[i%len(other)]) } return prod } ================================================ FILE: data/vector_bn256.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package data import ( "math/big" "github.com/fentec-project/bn256" ) // VectorG1 wraps a slice of elements from elliptic curve BN256.G1 group. type VectorG1 []*bn256.G1 // Add sums vectors v1 and v2 (also v1 * v2 in multiplicative notation). // It returns the result in a new VectorG1 instance. func (v VectorG1) Add(other VectorG1) VectorG1 { sum := make(VectorG1, len(v)) for i := range sum { sum[i] = new(bn256.G1).Add(v[i], other[i]) } return sum } // Neg returns a new VectorG1 instance with // values -v in the additive notation. func (v VectorG1) Neg() VectorG1 { neg := make(VectorG1, len(v)) for i := range neg { neg[i] = new(bn256.G1).Neg(v[i]) } return neg } // Copy produces a new copy of vector v. func (v VectorG1) Copy() VectorG1 { cp := make(VectorG1, len(v)) for i := range cp { cp[i] = new(bn256.G1).Set(v[i]) } return cp } // MulScalar multiplies s * v (in additive notation). func (v VectorG1) MulScalar(s *big.Int) VectorG1 { sTmp := new(big.Int).Set(s) out := v.Copy() if s.Sign() == -1 { sTmp.Neg(s) out = out.Neg() } for i := range out { out[i].ScalarMult(out[i], sTmp) } return out } // VectorG2 wraps a slice of elements from elliptic curve BN256.G2 group. type VectorG2 []*bn256.G2 // Add sums vectors v1 and v2 (also v1 * v2 in multiplicative notation). // It returns the result in a new VectorG2 instance. func (v VectorG2) Add(other VectorG2) VectorG2 { sum := make(VectorG2, len(v)) for i := range sum { sum[i] = new(bn256.G2).Add(v[i], other[i]) } return sum } // Neg returns a new VectorG1 instance with // values -v in the additive notation. func (v VectorG2) Neg() VectorG2 { neg := make(VectorG2, len(v)) for i := range neg { neg[i] = new(bn256.G2).Neg(v[i]) } return neg } // Copy produces a new copy of vector v. func (v VectorG2) Copy() VectorG2 { cp := make(VectorG2, len(v)) for i := range cp { cp[i] = new(bn256.G2).Set(v[i]) } return cp } // MulScalar multiplies s * v (in additive notation). func (v VectorG2) MulScalar(s *big.Int) VectorG2 { sTmp := new(big.Int).Set(s) out := v.Copy() if s.Sign() == -1 { sTmp.Neg(s) out = out.Neg() } for i := range out { out[i].ScalarMult(out[i], sTmp) } return out } // VectorGT wraps a slice of elements from pairing BN256.GT group. type VectorGT []*bn256.GT // Dot multiplies v = (v_1,...,v_n) and other = (o_1,...,o_n) to // return v1 * o_1 + ... + v_n *o_n (in additive notation) func (v VectorGT) Dot(other Vector) *bn256.GT { prod := new(bn256.GT).ScalarBaseMult(big.NewInt(0)) for i, c := range v { prod.Add(prod, new(bn256.GT).ScalarMult(c, other[i])) } return prod } ================================================ FILE: data/vector_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package data import ( "math/big" "testing" "github.com/fentec-project/gofe/sample" "github.com/stretchr/testify/assert" ) func TestVector(t *testing.T) { l := 3 bound := new(big.Int).Exp(big.NewInt(2), big.NewInt(20), big.NewInt(0)) sampler := sample.NewUniform(bound) x, err := NewRandomVector(l, sampler) if err != nil { t.Fatalf("Error during random generation: %v", err) } y, err := NewRandomVector(l, sampler) if err != nil { t.Fatalf("Error during random generation: %v", err) } add := x.Add(y) mul, err := x.Dot(y) if err != nil { t.Fatalf("Error during vector multiplication: %v", err) } modulo := int64(104729) mod := x.Mod(big.NewInt(modulo)) innerProd := big.NewInt(0) for i := 0; i < 3; i++ { assert.Equal(t, new(big.Int).Add(x[i], y[i]), add[i], "coordinates should sum correctly") innerProd = innerProd.Add(innerProd, new(big.Int).Mul(x[i], y[i])) assert.Equal(t, new(big.Int).Mod(x[i], big.NewInt(modulo)), mod[i], "coordinates should mod correctly") } assert.Equal(t, innerProd, mul, "inner product should calculate correctly") sampler = sample.NewUniform(big.NewInt(256)) var key [32]byte for i := range key { r, _ := sampler.Sample() key[i] = byte(r.Int64()) } _, err = NewRandomDetVector(100, big.NewInt(5), &key) assert.Equal(t, err, nil) } func TestVector_MulAsPolyInRing(t *testing.T) { p1 := Vector{big.NewInt(0), big.NewInt(1), big.NewInt(2)} p2 := Vector{big.NewInt(2), big.NewInt(1), big.NewInt(0)} prod, _ := p1.MulAsPolyInRing(p2) assert.Equal(t, prod, Vector{big.NewInt(-2), big.NewInt(2), big.NewInt(5)}) } func TestVecor_Tensor(t *testing.T) { v1 := Vector{big.NewInt(1), big.NewInt(2)} v2 := Vector{big.NewInt(1), big.NewInt(2)} prodExpected := Vector{big.NewInt(1), big.NewInt(2), big.NewInt(2), big.NewInt(4)} prod := v1.Tensor(v2) assert.Equal(t, prodExpected, prod, "tensor product of vectors does not work correctly") } ================================================ FILE: doc.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ // Package gofe provides implementations of functional encryption // schemes for linear and quadratic polynomials. package gofe ================================================ FILE: go.mod ================================================ module github.com/fentec-project/gofe go 1.17 require ( github.com/fentec-project/bn256 v0.0.0-20190726093940-0d0fc8bfeed0 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.7.0 golang.org/x/crypto v0.0.0-20211115234514-b4de73f9ece8 ) require ( github.com/davecgh/go-spew v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect ) ================================================ FILE: go.sum ================================================ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fentec-project/bn256 v0.0.0-20190726093940-0d0fc8bfeed0 h1:mkWVpEiA+MMlWxElUXRqTVSH9eETZvqJ21NZTaDaMiI= github.com/fentec-project/bn256 v0.0.0-20190726093940-0d0fc8bfeed0/go.mod h1:llEBqR6SDQxLj2lH10BjIYPrcoqDAFv6mhsqvHfIzlI= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/crypto v0.0.0-20211115234514-b4de73f9ece8 h1:5QRxNnVsaJP6NAse0UdkRgL3zHMvCRRkrDVLNdNpdy4= golang.org/x/crypto v0.0.0-20211115234514-b4de73f9ece8/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= ================================================ FILE: innerprod/doc.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ // Package innerprod includes functional encryption schemes for inner // products. // // Based on security assumptions, the schemes are organized into // subpackages simple (s-IND-CPA security), and fullysec // (e.g. "fully secure", offering adaptive IND-CPA security). // // Note that in both packages you will find single input as // well as multi input schemes. Construction of all multi input // schemes is based on the work of Abdalla et. al (see paper: // https://eprint.iacr.org/2017/972.pdf) package innerprod ================================================ FILE: innerprod/fullysec/damgard.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package fullysec import ( "fmt" "math/big" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/internal" "github.com/fentec-project/gofe/internal/dlog" "github.com/fentec-project/gofe/internal/keygen" "github.com/fentec-project/gofe/sample" ) // DamgardParams includes public parameters for the Damgard inner // product scheme. // L (int): The length of vectors to be encrypted. // Bound (int): The value by which coordinates of vectors x and y are bounded. // G (int): Generator of a cyclic group Z_P: G**(Q) = 1 (mod P). // H (int): Generator of a cyclic group Z_P: H**(Q) = 1 (mod P). // P (int): Modulus - we are operating in a cyclic group Z_P. // Q (int): Multiplicative order of G and H. type DamgardParams struct { L int Bound *big.Int G *big.Int H *big.Int P *big.Int Q *big.Int } // Damgard represents a scheme instantiated from the DDH assumption // based on DDH variant of: // Agrawal, Shweta, Libert, and Stehle: // "Fully secure functional encryption for inner products, // from standard assumptions". type Damgard struct { Params *DamgardParams } // NewDamgard configures a new instance of the scheme. // It accepts the length of input vectors l, the bit length of the // modulus (we are operating in the Z_p group), and a bound by which // coordinates of input vectors are bounded. // // It returns an error in case the scheme could not be properly // configured, or if precondition l * bound² is >= order of the cyclic // group. func NewDamgard(l, modulusLength int, bound *big.Int) (*Damgard, error) { key, err := keygen.NewElGamal(modulusLength) if err != nil { return nil, err } zero := big.NewInt(0) one := big.NewInt(1) two := big.NewInt(2) bSquared := new(big.Int).Exp(bound, two, nil) prod := new(big.Int).Mul(big.NewInt(int64(2*l)), bSquared) if prod.Cmp(key.Q) > 0 { return nil, fmt.Errorf("2 * l * bound^2 should be smaller than group order") } h := new(big.Int) for { sampler := sample.NewUniformRange(two, key.Q) r, err := sampler.Sample() if err != nil { return nil, err } // h generated in the following way is always a generator with order q h.Exp(key.G, r, key.P) // additional checks to avoid some known attacks if new(big.Int).Mod(new(big.Int).Sub(key.P, one), h).Cmp(zero) == 0 { continue } hInv := new(big.Int).ModInverse(h, key.P) if new(big.Int).Mod(new(big.Int).Sub(key.P, one), hInv).Cmp(zero) == 0 { continue } break } return &Damgard{ Params: &DamgardParams{ L: l, Bound: bound, G: key.G, H: h, P: key.P, Q: key.Q, }, }, nil } // NewDamgardPrecomp configures a new instance of the scheme based on // precomputed prime numbers and generators. // It accepts the length of input vectors l, the bit length of the // modulus (we are operating in the Z_p group), and a bound by which // coordinates of input vectors are bounded. The modulus length should // be one of values 1024, 1536, 2048, 2560, 3072, or 4096. The precomputed // prime numbers and generators were simply obtained by running NewDamgard // function. // // It returns an error in case the scheme could not be properly // configured, or if precondition l * bound² is >= order of the cyclic // group. func NewDamgardPrecomp(l, modulusLength int, bound *big.Int) (*Damgard, error) { one := big.NewInt(1) two := big.NewInt(2) g := new(big.Int) h := new(big.Int) p := new(big.Int) if modulusLength == 1024 { g.SetString("34902160241479276675539633849372382885917193816560610471607073855548755350834003692485735908635894317735639518678334280193650806072183057417077181724192674928134805218882803812978345229222559213790765817899845072682155064387311523738581388872686127675360979304234957611566801734164757915959042140104663977828", 10) h.SetString("15420637599119437909472314391464117244272797175081469344935256550115202257944676665553901111021364396888996244331294748348639524453606317448549098209774792643437849812948134158610698367936877182339463164515756739059909678247548635902607622196992717889883047826579537373927574110724167611850755082659113353814", 10) p.SetString("166211269243229118758738154756726384542659478479960313411107431885216572625212662756677338184675400324411541201832214281445670912135683272416408753424543622705770319923251281963485084208425069817917631106045349238686234860629044433560424091289406000897029571960128048529362925472176997104870527051276406995203", 10) } else if modulusLength == 1536 { g.SetString("676416913692519694440150163403654362412279108516867264953779609011365998625435399420336578530015558254310139891236630566729665914687641028600402606957815727025192669238117788115237116562468680376464346714542467465836552396661693422160454402926392749202926871877212792118140354124110927269910674002861908621272286950597240072605316784317536178700101838123530590145680002962405974024190384775185108002307650499125333676880320808656556635493186351335151559453463208", 10) h.SetString("162435356591098080112923949933138473281129080012462876365242296621510003247926800168810467125425312741356845555051398168995345221821254788094709212569083281560861934370026731898947082535853089500762419810631794616608702382341836751059467526394750733484031555507059435514454850206195181859188711511695415306637351122791298103837179337333322817996915750092675310561842474481585085713281105392293555407844591189296291324552036671109998940504540100161197823599243891", 10) p.SetString("1851297899986638926486011430658634631676522135433726749065856232802142091866650774719879427474637700607873256035038534449089405369134066444876856913629831069906506096279113968447116822488133963417347136141052507685108634240736100862550194947326287783557220764070479431781692630708747550712729778398000353165406458520850089303530985563143326919073190605085889925484113854496074216626577246143598303709289292397203458923541841135799203967503522114881404128535647507", 10) } else if modulusLength == 2048 { g.SetString("4006960929413042209594165215465319088439374252008797022450541422457034721098828647078778605657155669917104962611933792130890703423519992986737966991597160684973795472419962788730248050852176194215699504914899438223683843401963466624139534923052671383315398134823370041633710463630745156269175253639670460050105594663691338308037509280576148624454011047879615100156717631945194107791315234171086603775159708325087759679758438868772220133433497821899045165244202228696902434100209752952701657306825368599999359102329396520012735146260911352901326915877502873633420811221206110021993351144711002138373506576799781061829", 10) h.SetString("16531397285674350767314702525644915041456240916042269006661016218466751820121367708744665988146094312460459686332899317211264306268688887145182608791297975910105233621322826639428591684097281721326762853220049531036892964645338402521408932284623118973840687103995270506055864839392780688919921574729334863152272712825111303515621796552988450890438067795005159750945818303100847992123152427222482913524403644261704298722743429153705746979942035168013929376597721413830914117313871470498590418693493905967182265847426629997026054543887503362398742858491325979767814095393383666927420156454285653125386823358683247619671", 10) p.SetString("28884237504713658990682089080899862128005980675308910325841161962760155725037929764087367167449843609136681034352509183117742758446654629096509285354423361556493020266963222508540306384896802796001914743293196010488452478370041404523014215612960481024232879327123268440037633547483165934132901270561772860319969916305482525766132307669097012989986613879246932730824899649301621408341438037745468033187743673001187803377254713546325789438300798311106106322698517805307792059495696632070953526611920926003483451787562399452650878943515646786958216714025307572678422373120397225912926110031401983688860264234966561627699", 10) } else if modulusLength == 2560 { g.SetString("283408881721750179985507845260248881237898607313021593637913985490973593382472548378053368228310040484438920416918021737085067966444840449068073874437662089659563355479608930182263107110969154912883370207053052795964658868443319273167870311282045348396320159912742394374241864712808383029954025256232806465551969466207671603658677963161975454703127476120201164519187150268352527923664649275471494757270139533433456630363925187498055211365480086561354743681517539297815712218419607006668655891574362066382949706266666189227897710299445185100212256741698216505337617571970963008519334554537811591236478130526432239803909461119767954934793813410765013072006162612226471775059215628326278458577643374735250370115470812597459244082296191871275203831471332697557979904062571849", 10) h.SetString("60603226626244984946015074611399313803245697048858823305998791050960516381707120286785720252208953282756722645401890645185550185014757676697220190895358672768431707069471110996253922983573984124316810681408685350886144002884260922773441021522086311309748129811838078781339054390074111039635714653119513182362870864625376495724028601281359016299631376501745572486338940082085075668612842777510700862160795716160806125722301037965086971367697165162183431144060195434995026056781565232838274115314469024436666926374971029865332455354830888647688402585402990945621351542202067894748432776518006655843916388537951718725340446233164828852013369347111758769617449735121060447630493643403400643959220152005725907208460642317320745167072023561550218217956652950353398499254599742", 10) p.SetString("403126381462544353337185732949672984701995200694926494258456907009253008321275627278199160008680481542251933923210212978304159426174307419863781623411302777276318286800690060166638633211627521530173324015527027650548199185539205697958056639406116068885574865579676651743820636201007864067569576455725489531113260031526605827601510665037511961715114944815619491261828558745083975042855063688267346905844510423020844412350570902289599734320004108557140241966071165594059732527795488131297017205383953304055105007982366596746708951250486384299368612656872813778220074826250625689603663742175288397398948456522281031888042417278385238985218731264092285591879578299600853004336936458454638992426900228708418575870946630137618851131144232868141478901063119847104013555395370887", 10) } else if modulusLength == 3072 { g.SetString("3696261717511684685041982088526264061294500298114921057816954458306445697150348237912729036967670872345042594238223317055749478029025374644864924550052402546275985983344583674703146236623453822520422465163020824494790581472736649085281450730460445260696087738043872307629635997875332076478424042345012004769107421873566499123042621978973433575500345010912635742477932006291250637245855027695943163956584173316781442078828050076620331751405548730676363847526959436516279320074682721438642683731766682502490935962962293815202487144775533102010333956118641968798500514719248831145108532912211817219793191951880318961073149276914867129023978524587935704313755469570162971499124682746476415187933097132047611840762510892175328320025164466873845777990557296853549970943298347924080102740724512079409979152285019931666423541870247789529268168448010024121369388707140296446100906359619586133848407970098685310317291828335700424602208", 10) h.SetString("567649039783047480227338473928434756096941793851205367034019596755160950347349183458824085567577717539018304618223485608741105961797110596645149404693961630602136531678036354529574048049387247758318737991227930011620451360293879761672154747784975220369021073972313352929486325631485787131058176462649454317449501920846554754923439009414476391939400343349482311343015897922534760972817588470659148393369453541539857229554365836585067664597417625404552690944486027240221141785988408230137708791844568226402922187996696503785944931820332617839757682381094642917330498053626106798012177968190876620737530689308284333593333125689734905825145119037797011061634852469676220493758348073580817216823443614368951436342741042435481507815482798733572408751161565276006768858533125203508129436766572987021821650216162035709837972268600512594452400036134765839973589301700524293661567605165762293059122431279400652446300525682001019196136", 10) p.SetString("4387756306134544957818467663802660683665166110605728231080818705443663402154316615145921798856363268744945754470238000282108344905251127487705736550297997444150840902348669718478564904142834154197029830975532074167513046443903186309497214496864577129616824062991068960005865144004932069025136224356325248036029606434443391988386519658751798077031844645051726026696307027395796695909035405241040411794836124123435225690961994089776517262574417789067836840997650095451062948856617211542724543995145259735683916440579956961657374517806591607068842498749297993409884001044324428640569001916341503645559748760311343179943896427393009949062735145363544745972252566600994034655540841225414736222780096833045470605544717177880459300618917961703559234544541206877026518430276932498602360341258899345739335298856394124351357206871568254540730107127298623178526868418799471896060015463201459762913197633841160710893895836663035998106119", 10) } else if modulusLength == 4096 { g.SetString("51665588681810560577916524923861643358980285220048008212528567741884121491554604183472728540139463099618903178110360757930742372390027135064809646425064896539133721148335557788263239281487173350543811713890328584918216783142094297306639941000480756707312457878765754357205186485080839623690156744636468433787780205323460166423447602447200754978133176713947189000663528355089645281397174452923418212485422962705227706103188302892660448134233848971142570881089940852441776074246332915421265800026335300100610273942459340241610730244726628211914068945587128124478812632725838440727321816905181830592204023095726270782834020990986443265625389712733369116937470448592846480352222814297792606318850361699893703272484112273500581408730519942517586496563772194165844831300501908379990979449691597045730512107756238377635183257797115883839801779086058652272455400286891699445584526719648220045380141260347316315487340493029966105973850214850475440630205768783542021741101804842248602349004364816943429122368563644935802417389995380389429997320053299323220481603252879925927515844929958940305561718295197935926645561977544440676439150126025681320050786964708227836328341875446457912905977470123640014345655062829575775837287500880054558386787", 10) h.SetString("653735051824112393106801492829043654983094810153936916566419297234893790474265474662670688710073348877202869152643983800310336936407217572878666006904653566429370273243928900524622200126090143285935697025102272467307580256707629199315519492688020243538476473287317884073943557740453015512750672402070439254723909940895097629630953500504933411614006835509266155433485165683944804036549172274160941459059251790260424550107135346982158930124883878553492540634625536558325828876220292338217093182644187426663586499630969144324838282516931412124575753931891717246058395623268249929461243359978135742469252500443989584744702727780893623276815417727584978028391169715443458364712495037724857002997953369015075369014382917440895283545982020768248209480534141352756164681807291197719347396848885385564846533227145898299352065862788210851139899108807494649254246247909836864209696565581846420881131639054554828603682225473062422081030815891806059849482326467150974089876248055494734946134244334370720707623963134127685862797676223995758737312498339183154611067568296397353652230198967761004520297654249905695573112921297631619886554083093006856350790194488256889122557656064371766221432744243315870629759094828247037312561620044735468234281300", 10) p.SetString("1022249395832567838406986294560330159176972202126664245047364146720891252715766488477689126342364655087193411078517616569887825896401401223927363505007778278205623713273194552498760148834874746839752870298152746450585455651115247220867383465863156721401567161663838310658875672995951663020449772454232797368263754624173026584111779206080723120076751471597509403139249260220696195263597156452889920392585797464801375940661326779247976331028637271512085826066667631423502199894046717721786935806581428328491087482664043743281068318459302242239861275878019857365021173868449409246193470959347916848019032536247915451026158871684654213802886886213841729258073333569276986893577214659899227179735448593265633219968622571880602115519942763955551007919826002851866939641065270816032435114864853636918330698605282572789904941484540512478406984407320963402583009124880812235841866246441862987563989772424040933513333746472128494254253767426962063553015635240386636751473945937412527996558505231385625318878887383161350102080329744822052478052004574860361461762694379860797225344866320388590336321515376486033237159694567932935601775209663052272120524337888258857351777348841323194553467226791591208931619058871750498804369190487499494069660723", 10) } else { return nil, fmt.Errorf("modulus length should be one of values 1024, 1536, 2048, 2560, 3072, or 4096") } q := new(big.Int).Sub(p, one) q.Div(q, two) bSquared := new(big.Int).Exp(bound, two, nil) prod := new(big.Int).Mul(big.NewInt(int64(2*l)), bSquared) if prod.Cmp(q) > 0 { return nil, fmt.Errorf("2 * l * bound^2 should be smaller than group order") } return &Damgard{ Params: &DamgardParams{ L: l, Bound: bound, G: g, H: h, P: p, Q: q, }, }, nil } // NewDamgardFromParams takes configuration parameters of an existing // Damgard scheme instance, and reconstructs the scheme with same configuration // parameters. It returns a new Damgard instance. func NewDamgardFromParams(params *DamgardParams) *Damgard { return &Damgard{ Params: params, } } // DamgardSecKey is a secret key for Damgard scheme. type DamgardSecKey struct { S data.Vector T data.Vector } // GenerateMasterKeys generates a master secret key and master // public key for the scheme. It returns an error in case master keys // could not be generated. func (d *Damgard) GenerateMasterKeys() (*DamgardSecKey, data.Vector, error) { // both part of masterSecretKey mskS := make(data.Vector, d.Params.L) mskT := make(data.Vector, d.Params.L) masterPubKey := make([]*big.Int, d.Params.L) sampler := sample.NewUniformRange(big.NewInt(2), d.Params.Q) for i := 0; i < d.Params.L; i++ { s, err := sampler.Sample() if err != nil { return nil, nil, err } mskS[i] = s t, err := sampler.Sample() if err != nil { return nil, nil, err } mskT[i] = t y1 := new(big.Int).Exp(d.Params.G, s, d.Params.P) y2 := new(big.Int).Exp(d.Params.H, t, d.Params.P) masterPubKey[i] = new(big.Int).Mod(new(big.Int).Mul(y1, y2), d.Params.P) } return &DamgardSecKey{S: mskS, T: mskT}, masterPubKey, nil } // DamgardDerivedKey is a functional encryption key for Damgard scheme. type DamgardDerivedKey struct { Key1 *big.Int Key2 *big.Int } // DeriveKey takes master secret key and input vector y, and returns the // functional encryption key. In case the key could not be derived, it // returns an error. func (d *Damgard) DeriveKey(masterSecKey *DamgardSecKey, y data.Vector) (*DamgardDerivedKey, error) { if err := y.CheckBound(d.Params.Bound); err != nil { return nil, err } key1, err := masterSecKey.S.Dot(y) if err != nil { return nil, err } key2, err := masterSecKey.T.Dot(y) if err != nil { return nil, err } k1 := new(big.Int).Mod(key1, d.Params.Q) k2 := new(big.Int).Mod(key2, d.Params.Q) return &DamgardDerivedKey{Key1: k1, Key2: k2}, nil } // Encrypt encrypts input vector x with the provided master public key. // It returns a ciphertext vector. If encryption failed, error is returned. func (d *Damgard) Encrypt(x, masterPubKey data.Vector) (data.Vector, error) { if err := x.CheckBound(d.Params.Bound); err != nil { return nil, err } sampler := sample.NewUniformRange(big.NewInt(2), d.Params.Q) r, err := sampler.Sample() if err != nil { return nil, err } ciphertext := make([]*big.Int, len(x)+2) // c = g^r // dd = h^r c := new(big.Int).Exp(d.Params.G, r, d.Params.P) ciphertext[0] = c dd := new(big.Int).Exp(d.Params.H, r, d.Params.P) ciphertext[1] = dd for i := 0; i < len(x); i++ { // e_i = h_i^r * g^x_i // e_i = mpk[i]^r * g^x_i t1 := new(big.Int).Exp(masterPubKey[i], r, d.Params.P) t2 := internal.ModExp(d.Params.G, x[i], d.Params.P) ct := new(big.Int).Mod(new(big.Int).Mul(t1, t2), d.Params.P) ciphertext[i+2] = ct } return data.NewVector(ciphertext), nil } // Decrypt accepts the encrypted vector, functional encryption key, and // a plaintext vector y. It returns the inner product of x and y. // If decryption failed, error is returned. func (d *Damgard) Decrypt(cipher data.Vector, key *DamgardDerivedKey, y data.Vector) (*big.Int, error) { if err := y.CheckBound(d.Params.Bound); err != nil { return nil, err } num := big.NewInt(1) for i, ct := range cipher[2:] { t1 := internal.ModExp(ct, y[i], d.Params.P) num = num.Mod(new(big.Int).Mul(num, t1), d.Params.P) } t1 := new(big.Int).Exp(cipher[0], key.Key1, d.Params.P) t2 := new(big.Int).Exp(cipher[1], key.Key2, d.Params.P) denom := new(big.Int).Mod(new(big.Int).Mul(t1, t2), d.Params.P) denomInv := new(big.Int).ModInverse(denom, d.Params.P) r := new(big.Int).Mod(new(big.Int).Mul(num, denomInv), d.Params.P) bSquared := new(big.Int).Exp(d.Params.Bound, big.NewInt(2), big.NewInt(0)) bound := new(big.Int).Mul(big.NewInt(int64(d.Params.L)), bSquared) calc, err := dlog.NewCalc().InZp(d.Params.P, d.Params.Q) if err != nil { return nil, err } calc = calc.WithNeg() res, err := calc.WithBound(bound).BabyStepGiantStep(r, d.Params.G) return res, err } ================================================ FILE: innerprod/fullysec/damgard_dec_multi.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package fullysec import ( "crypto/sha256" "fmt" "math/big" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/sample" ) // DamgardDecMultiClient represents a client in a decentralized // multi client variant of the underlying multi client (dmagard_multi) // scheme based. The decentralization is based on // Abdalla, Benhamouda, Kohlweiss,and Waldner: // "Decentralizing Inner-Product Functional Encryption". // The participants in the scheme are clients without a central authority. // They interactively generate private keys for each client so that client i // can encrypt vector x_i. The scheme allows the clients to interactively // generate a key_Y, depending on a matrix Y with rows y_i, so that // given key_y and the ciphertext the decryptor can compute value // Σ_i (sum of dot products). type DamgardDecMultiClient struct { // number of encryptors Idx int *DamgardMulti ClientPubKey *big.Int ClientSecKey *big.Int Share data.Matrix } // NewDamgardDecMultiClient configures a new client in the decentralized scheme // based on a underlying DamgardMulti scheme. // It accepts the identification of the client (an integer from [0, numClients)) // and the underlying DamgardMulti scheme (which contains all the shared parameters) // // It returns an error in case the scheme cannot be properly initialized. func NewDamgardDecMultiClient(idx int, damgardMulti *DamgardMulti) (*DamgardDecMultiClient, error) { sampler := sample.NewUniform(damgardMulti.Params.Q) sec, err := sampler.Sample() if err != nil { return nil, fmt.Errorf("could not generate random value") } pub := new(big.Int).Exp(damgardMulti.Params.G, sec, damgardMulti.Params.P) return &DamgardDecMultiClient{ Idx: idx, DamgardMulti: damgardMulti, ClientPubKey: pub, ClientSecKey: sec, }, nil } // SetShare sets a shared key for client c, based on the public keys of all the // clients involved in the scheme. It assumes that Idx of a client indicates // which is the corresponding public key in pubKeys. Shared keys are such that // each client has a random key but all the shared keys sum to 0. func (c *DamgardDecMultiClient) SetShare(pubKeys []*big.Int) error { c.Share = data.NewConstantMatrix(c.NumClients, c.Params.L, big.NewInt(0)) var add data.Matrix var err error for k := 0; k < len(pubKeys); k++ { if k == c.Idx { continue } sharedNum := new(big.Int).Exp(pubKeys[k], c.ClientSecKey, c.Params.P) sharedKey := sha256.Sum256([]byte(sharedNum.String())) add, err = data.NewRandomDetMatrix(c.NumClients, c.Params.L, c.Params.Q, &sharedKey) if err != nil { return err } if k < c.Idx { c.Share, err = c.Share.Add(add) if err != nil { return err } } else { c.Share, err = c.Share.Sub(add) if err != nil { return err } } c.Share = c.Share.Mod(c.Params.Q) } return nil } // DamgardDecMultiSecKey is a secret key that each client has. type DamgardDecMultiSecKey struct { sk *DamgardSecKey pk data.Vector OtpKey data.Vector } // GenerateKeys generates the secret key for each client. // // It returns an error in case master keys could not be generated. func (c *DamgardDecMultiClient) GenerateKeys() (*DamgardDecMultiSecKey, error) { masterSecretKey, masterPublicKey, err := c.Damgard.GenerateMasterKeys() if err != nil { return nil, fmt.Errorf("error in master key generation") } otpVector, err := data.NewRandomVector(c.Damgard.Params.L, sample.NewUniform(c.Damgard.Params.Q)) if err != nil { return nil, fmt.Errorf("error in random vector generation") } return &DamgardDecMultiSecKey{sk: masterSecretKey, pk: masterPublicKey, OtpKey: otpVector}, nil } // Encrypt generates a ciphertext from the input vector x // with the provided secret key. // If encryption failed, error is returned. func (c *DamgardDecMultiClient) Encrypt(x data.Vector, key *DamgardDecMultiSecKey) (data.Vector, error) { if err := x.CheckBound(c.Params.Bound); err != nil { return nil, err } xAddOtp := x.Add(key.OtpKey) otpModulo := xAddOtp.Mod(c.Params.Q) return c.Damgard.Encrypt(otpModulo, key.pk) } // DamgardDecMultiDerivedKeyPart is functional encryption key for decentralized // Damgrad Scheme. type DamgardDecMultiDerivedKeyPart struct { KeyPart *DamgardDerivedKey OTPKeyPart *big.Int } // DeriveKeyShare is run by a client. It takes a secret key and // a matrix y comprised of input vectors, and returns a part of // the functional encryption key. // In case the key could not be derived, it returns an error. func (c *DamgardDecMultiClient) DeriveKeyShare(secKey *DamgardDecMultiSecKey, y data.Matrix) (*DamgardDecMultiDerivedKeyPart, error) { if err := y.CheckBound(c.Damgard.Params.Bound); err != nil { return nil, err } yPart := data.NewVector(y[c.Idx]) z1, err := secKey.OtpKey.Dot(yPart) if err != nil { return nil, err } z2, err := c.Share.Dot(y) if err != nil { return nil, err } zPart := new(big.Int).Add(z1, z2) zPart.Mod(zPart, c.Damgard.Params.Q) key, err := c.Damgard.DeriveKey(secKey.sk, yPart) if err != nil { return nil, err } return &DamgardDecMultiDerivedKeyPart{key, zPart}, nil } // DamgardDecMultiDec represents a decryptor for the decentralized variant of the // underlying multi input Damgard scheme. type DamgardDecMultiDec struct { *DamgardMulti } // NewDamgardDecMultiDec takes the underlying DamgardMulti and instantiates a // new DamgardDecMultiDec struct. func NewDamgardDecMultiDec(damgardMulti *DamgardMulti) *DamgardDecMultiDec { return &DamgardDecMultiDec{ DamgardMulti: NewDamgardMultiFromParams(damgardMulti.NumClients, damgardMulti.Bound, damgardMulti.Params), } } // Decrypt accepts an array of ciphertexts comprised of encrypted vectors, // an array of partial functional encryption keys, and a matrix y representing // the inner-product vectors. It returns the sum of inner products. // If decryption failed, an error is returned. func (dc *DamgardDecMultiDec) Decrypt(cipher []data.Vector, partKeys []*DamgardDecMultiDerivedKeyPart, y data.Matrix) (*big.Int, error) { if err := y.CheckBound(dc.Params.Bound); err != nil { return nil, err } if len(cipher) != len(partKeys) { return nil, fmt.Errorf("the number of keys does not match the number of ciphertexts") } keys := make([]*DamgardDerivedKey, len(partKeys)) z := big.NewInt(0) for i := 0; i < len(partKeys); i++ { z.Add(z, partKeys[i].OTPKeyPart) keys[i] = partKeys[i].KeyPart } z.Mod(z, dc.Params.Q) key := &DamgardMultiDerivedKey{Keys: keys, Z: z, } return dc.DamgardMulti.Decrypt(cipher, key, y) } ================================================ FILE: innerprod/fullysec/damgard_dec_multi_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package fullysec_test import ( "math/big" "testing" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/innerprod/fullysec" "github.com/fentec-project/gofe/sample" "github.com/stretchr/testify/assert" ) func testFullySecDamgardDecMultiFromParam(t *testing.T, param damgardTestParam) { // choose parameters numOfClients := 10 l := 2 bound := big.NewInt(1024) sampler := sample.NewUniformRange(new(big.Int).Add(new(big.Int).Neg(bound), big.NewInt(1)), bound) // create a (non-decentralized) multi-input scheme as the underlying scheme // for the decentralization var damgardMulti *fullysec.DamgardMulti var err error if param.precomputed { // modulusLength defines the security of the scheme, the higher the better damgardMulti, err = fullysec.NewDamgardMultiPrecomp(numOfClients, l, param.modulusLength, bound) } else { damgardMulti, err = fullysec.NewDamgardMulti(numOfClients, l, param.modulusLength, bound) } assert.NoError(t, err) // we simulate different independent, decentralized clients and // we collect all of their public keys clients := make([]*fullysec.DamgardDecMultiClient, numOfClients) pubKeys := make([]*big.Int, numOfClients) for i := 0; i < numOfClients; i++ { clients[i], err = fullysec.NewDamgardDecMultiClient(i, damgardMulti) assert.NoError(t, err) pubKeys[i] = clients[i].ClientPubKey } // each client makes a private partial key out of the public keys and // create its own secret key for the encryption of a vector secKeys := make([]*fullysec.DamgardDecMultiSecKey, numOfClients) for i := 0; i < numOfClients; i++ { err = clients[i].SetShare(pubKeys) if err != nil { t.Fatalf("Error during share generation: %v", err) } secKeys[i], err = clients[i].GenerateKeys() if err != nil { t.Fatalf("Error during secret keys generation: %v", err) } } // each client encrypts its own vector x_i ciphertexts := make([]data.Vector, numOfClients) collectedX := make([]data.Vector, numOfClients) // for checking whether encrypt/decrypt works properly for i := 0; i < numOfClients; i++ { x, err := data.NewRandomVector(l, sampler) // x possessed and chosen by encryptors[i] if err != nil { t.Fatalf("Error during random vector generation: %v", err) } collectedX[i] = x c, err := clients[i].Encrypt(x, secKeys[i]) if err != nil { t.Fatalf("Error during encryption: %v", err) } ciphertexts[i] = c } // pick a matrix that represent the collection of inner-product vectors y_i y, err := data.NewRandomMatrix(numOfClients, l, sampler) assert.NoError(t, err) partKeys := make([]*fullysec.DamgardDecMultiDerivedKeyPart, numOfClients) for i := 0; i < numOfClients; i++ { partKeys[i], err = clients[i].DeriveKeyShare(secKeys[i], y) if err != nil { t.Fatalf("Error during derivation of key: %v", err) } } // we simulate the decryptor decryptor := fullysec.NewDamgardDecMultiDec(damgardMulti) // decryptor decrypts the value xy, err := decryptor.Decrypt(ciphertexts, partKeys, y) if err != nil { t.Fatalf("Error during decryption: %v", err) } // we check if the decrypted value is correct xMatrix, err := data.NewMatrix(collectedX) if err != nil { t.Fatalf("Error during collection of vectors to be encrypted: %v", err) } xyCheck, err := xMatrix.Dot(y) if err != nil { t.Fatalf("Error during inner product calculation: %v", err) } assert.Equal(t, xy.Cmp(xyCheck), 0, "obtained incorrect inner product") } func TestFullySec_DamgardDecMulti(t *testing.T) { params := []damgardTestParam{{name: "random", modulusLength: 512, precomputed: false}, {name: "precomputed", modulusLength: 2048, precomputed: true}} for _, param := range params { t.Run(param.name, func(t *testing.T) { testFullySecDamgardDecMultiFromParam(t, param) }) } } ================================================ FILE: innerprod/fullysec/damgard_multi.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package fullysec import ( "fmt" "math/big" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/internal" "github.com/fentec-project/gofe/internal/dlog" "github.com/fentec-project/gofe/sample" ) // DamgardMulti represents a multi input variant of the // underlying Damgard scheme based on // Abdalla, Catalano, Fiore, Gay, and Ursu: // "Multi-Input Functional Encryption for Inner Products: // Function-Hiding Realizations and Constructions without Pairings". // The participants in the scheme are clients and a central authority. // The central authority generates keys for each client so that client i // encrypts vector x_i. The scheme allows the central authority to // generate a key_Y, depending on a matrix Y with rows y_i, so that // given key_y and the ciphertext the decryptor can compute value // Σ_i (sum of dot products). // DamgardMulti is a struct in DamgardMulti scheme, that holds // all the shared parameters, and can represent the central authority // or the decryptor. type DamgardMulti struct { // number of clients NumClients int Bound *big.Int *Damgard } // DamgardMultiClient represents a single client for the DamgardMulti scheme. type DamgardMultiClient struct { Bound *big.Int *Damgard } // NewDamgardMulti configures a new instance of the scheme. // It accepts the number of clients, the length of // input vectors l, the bit length of the modulus (we are // operating in the Z_p group), and a bound by which coordinates // of input vectors are bounded. It generates all the remaining // parameters to be shared. // // It returns an error in case the underlying Damgard scheme // instances could not be properly instantiated. func NewDamgardMulti(numClients, l, modulusLength int, bound *big.Int) (*DamgardMulti, error) { bSquared := new(big.Int).Exp(bound, big.NewInt(2), nil) prod := new(big.Int).Mul(big.NewInt(int64(l*numClients*2)), bSquared) damgard, err := NewDamgard(l, modulusLength, bound) if err != nil { return nil, err } if prod.Cmp(damgard.Params.Q) > 0 { return nil, fmt.Errorf("2 * l * numClients * bound^2 should be smaller than group order") } // the bound of the underlying Damgard scheme is set to // the maximum value since the scheme will be used to encrypt // values summed with one time pad, thus arbitrary big damgard.Params.Bound = damgard.Params.Q return &DamgardMulti{ NumClients: numClients, Bound: bound, Damgard: damgard, }, nil } // NewDamgardMultiPrecomp configures a new instance of the scheme // based on precomputed prime numbers and generators. // It accepts the number of clients, the length of // input vectors l, the bit length of the modulus (we are // operating in the Z_p group), and a bound by which coordinates // of input vectors are bounded. It generates all the remaining // parameters to be shared. The modulus length should // be one of values 1024, 1536, 2048, 2560, 3072, or 4096. The precomputed // prime numbers and generators were simply obtained by running NewDamgard // function. // // It returns an error in case the underlying Damgard scheme // instances could not be properly instantiated. func NewDamgardMultiPrecomp(numClients, l, modulusLength int, bound *big.Int) (*DamgardMulti, error) { bSquared := new(big.Int).Exp(bound, big.NewInt(2), nil) prod := new(big.Int).Mul(big.NewInt(int64(l*numClients*2)), bSquared) damgard, err := NewDamgardPrecomp(l, modulusLength, bound) if err != nil { return nil, err } if prod.Cmp(damgard.Params.Q) > 0 { return nil, fmt.Errorf("2 * l * numClients * bound^2 should be smaller than group order") } // the bound of the underlying Damgard scheme is set to // the maximum value since the scheme will be used to encrypt // values summed with one time pad, thus arbitrary big damgard.Params.Bound = damgard.Params.Q return &DamgardMulti{ NumClients: numClients, Bound: bound, Damgard: damgard, }, nil } // NewDamgardMultiClientFromParams takes the bound and configuration parameters of an underlying // Damgard scheme instance, and instantiates a new DamgardMultiClient. // // It returns a new DamgardMultiClient instance. func NewDamgardMultiClientFromParams(bound *big.Int, params *DamgardParams) *DamgardMultiClient { return &DamgardMultiClient{ Bound: bound, Damgard: &Damgard{params}, } } // NewDamgardMultiFromParams takes the number of clients, bound and configuration // parameters of an existing Damgard scheme instance, and reconstructs // the scheme with same configuration parameters. // // It returns a new DamgardMulti instance. func NewDamgardMultiFromParams(numClients int, bound *big.Int, params *DamgardParams) *DamgardMulti { return &DamgardMulti{ NumClients: numClients, Bound: bound, Damgard: &Damgard{params}, } } // DamgardMultiSecKeys is a struct containing keys and one time pads for all the clients in // the Damgard multi input scheme. type DamgardMultiSecKeys struct { Msk []*DamgardSecKey Mpk data.Matrix Otp data.Matrix } // GenerateMasterKeys generates keys and one time pads for all the clients. // // It returns an error in case values could not be generated. func (dm *DamgardMulti) GenerateMasterKeys() (*DamgardMultiSecKeys, error) { multiMsk := make([]*DamgardSecKey, dm.NumClients) multiMpk := make([]data.Vector, dm.NumClients) multiOtp := make([]data.Vector, dm.NumClients) for i := 0; i < dm.NumClients; i++ { msk, mpk, err := dm.Damgard.GenerateMasterKeys() if err != nil { return nil, fmt.Errorf("error in master key generation") } multiMsk[i] = msk multiMpk[i] = mpk otp, err := data.NewRandomVector(dm.Params.L, sample.NewUniform(dm.Params.Q)) if err != nil { return nil, fmt.Errorf("error in random vector generation") } multiOtp[i] = otp } secKeys := &DamgardMultiSecKeys{ Msk: multiMsk, Mpk: data.Matrix(multiMpk), Otp: data.Matrix(multiOtp), } return secKeys, nil } // Encrypt generates a ciphertext from the input vector x // with the provided public key of the underlying Damgard scheme and // one-time pad otp (which are a part of the secret key). It returns // the ciphertext vector. If the encryption failed, error is returned. func (e *DamgardMultiClient) Encrypt(x data.Vector, pubKey, otp data.Vector) (data.Vector, error) { if err := x.CheckBound(e.Params.Bound); err != nil { return nil, err } xAddOtp := x.Add(otp) xAddOtp = xAddOtp.Mod(e.Params.Q) return e.Damgard.Encrypt(xAddOtp, pubKey) } // DamgardMultiDerivedKey is a functional encryption key for DamgardMulti scheme. type DamgardMultiDerivedKey struct { Keys []*DamgardDerivedKey Z *big.Int // Σ where u_i is OTP key for i-th client } // DeriveKey takes master secret key and a matrix y comprised // of input vectors, and returns the functional encryption key. // In case the key could not be derived, it returns an error. func (dm *DamgardMulti) DeriveKey(secKey *DamgardMultiSecKeys, y data.Matrix) (*DamgardMultiDerivedKey, error) { if err := y.CheckBound(dm.Bound); err != nil { return nil, err } z, err := secKey.Otp.Dot(y) if err != nil { return nil, err } z.Mod(z, dm.Params.Q) derivedKeys := make([]*DamgardDerivedKey, dm.NumClients) for i := 0; i < dm.NumClients; i++ { derivedKey, err := dm.Damgard.DeriveKey(secKey.Msk[i], y[i]) if err != nil { return nil, err } derivedKeys[i] = derivedKey } return &DamgardMultiDerivedKey{derivedKeys, z}, nil } // Decrypt accepts an array of ciphers, i.e. an array of encrypted vectors, // functional encryption key, and a matrix y describing the inner-product. // It returns the sum of inner products Σ_i . // If decryption failed, error is returned. func (dm *DamgardMulti) Decrypt(cipher []data.Vector, key *DamgardMultiDerivedKey, y data.Matrix) (*big.Int, error) { if err := y.CheckBound(dm.Bound); err != nil { return nil, err } r := big.NewInt(1) for k := 0; k < dm.NumClients; k++ { num := big.NewInt(1) for i, ct := range cipher[k][2:] { t1 := internal.ModExp(ct, y[k][i], dm.Params.P) num = num.Mod(new(big.Int).Mul(num, t1), dm.Params.P) } t1 := new(big.Int).Exp(cipher[k][0], key.Keys[k].Key1, dm.Params.P) t2 := new(big.Int).Exp(cipher[k][1], key.Keys[k].Key2, dm.Params.P) denom := new(big.Int).Mod(new(big.Int).Mul(t1, t2), dm.Params.P) denomInv := new(big.Int).ModInverse(denom, dm.Params.P) r.Mul(r, denomInv) r.Mul(r, num) r.Mod(r, dm.Params.P) } zExp := new(big.Int).Exp(dm.Params.G, key.Z, dm.Params.P) zExpInv := new(big.Int).ModInverse(zExp, dm.Params.P) r.Mul(r, zExpInv) r.Mod(r, dm.Params.P) calc, err := dlog.NewCalc().InZp(dm.Params.P, dm.Params.Q) if err != nil { return nil, err } calc = calc.WithNeg() bound := new(big.Int).Mul(dm.Bound, dm.Bound) bound.Mul(bound, big.NewInt(int64(dm.Params.L*dm.NumClients))) res, err := calc.WithBound(bound).BabyStepGiantStep(r, dm.Params.G) return res, err } ================================================ FILE: innerprod/fullysec/damgard_multi_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package fullysec_test import ( "math/big" "testing" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/innerprod/fullysec" "github.com/fentec-project/gofe/sample" "github.com/stretchr/testify/assert" ) func testFullySecDamgardMultiDDHFromParam(t *testing.T, param damgardTestParam) { // choose meta-parameters for the scheme numClients := 6 l := 5 bound := big.NewInt(1024) sampler := sample.NewUniformRange(new(big.Int).Add(new(big.Int).Neg(bound), big.NewInt(1)), bound) // build the central authority for the scheme var damgardMulti *fullysec.DamgardMulti var err error if param.precomputed { // modulusLength defines the security of the scheme, the higher the better damgardMulti, err = fullysec.NewDamgardMultiPrecomp(numClients, l, param.modulusLength, bound) } else { damgardMulti, err = fullysec.NewDamgardMulti(numClients, l, param.modulusLength, bound) } if err != nil { t.Fatalf("Failed to initialize multi input inner product: %v", err) } // we simulate different clients which might be on different machines (this means "multi-input"), clients := make([]*fullysec.DamgardMultiClient, numClients) for i := 0; i < numClients; i++ { clients[i] = fullysec.NewDamgardMultiClientFromParams(bound, damgardMulti.Params) } // the central authority generates keys for all the clients secKeys, err := damgardMulti.GenerateMasterKeys() if err != nil { t.Fatalf("Error during keys generation: %v", err) } // pick a matrix that represent the collection of inner-product vectors y_i y, err := data.NewRandomMatrix(numClients, l, sampler) if err != nil { t.Fatalf("Error during matrix generation: %v", err) } // each client encrypts its vector x_i collectedX := make([]data.Vector, numClients) // solely for checking whether Encrypt/Decrypt works properly ciphertexts := make([]data.Vector, numClients) for i := 0; i < numClients; i++ { x, err := data.NewRandomVector(l, sampler) // x_i possessed and chosen by client[i] if err != nil { t.Fatalf("Error during random vector generation: %v", err) } collectedX[i] = x c, err := clients[i].Encrypt(x, secKeys.Mpk[i], secKeys.Otp[i]) if err != nil { t.Fatalf("Error during encryption: %v", err) } ciphertexts[i] = c } // central authority derives the key for the decryptor derivedKey, err := damgardMulti.DeriveKey(secKeys, y) if err != nil { t.Fatalf("Error during key derivation: %v", err) } // we simulate the decryptor decryptor := fullysec.NewDamgardMultiFromParams(numClients, bound, damgardMulti.Params) // decryptor decrypts the value xy, err := decryptor.Decrypt(ciphertexts, derivedKey, y) if err != nil { t.Fatalf("Error during decryption: %v", err) } // we check if the decrypted value is correct xMatrix, err := data.NewMatrix(collectedX) if err != nil { t.Fatalf("Error during collection of vectors to be encrypted: %v", err) } xyCheck, err := xMatrix.Dot(y) if err != nil { t.Fatalf("Error during inner product calculation: %v", err) } assert.Equal(t, xy.Cmp(xyCheck), 0, "obtained incorrect inner product") } func TestFullySec_DamgardMultiDDH(t *testing.T) { params := []damgardTestParam{{name: "random", modulusLength: 512, precomputed: false}, {name: "precomputed", modulusLength: 2048, precomputed: true}} for _, param := range params { t.Run(param.name, func(t *testing.T) { testFullySecDamgardMultiDDHFromParam(t, param) }) } } ================================================ FILE: innerprod/fullysec/damgard_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package fullysec_test import ( "math/big" "testing" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/innerprod/fullysec" "github.com/fentec-project/gofe/sample" "github.com/stretchr/testify/assert" ) type damgardTestParam struct { name string modulusLength int precomputed bool } func testFullySecDamgardDDHFromParam(t *testing.T, param damgardTestParam) { l := 16 bound := big.NewInt(1024) sampler := sample.NewUniformRange(new(big.Int).Add(new(big.Int).Neg(bound), big.NewInt(1)), bound) var damgard *fullysec.Damgard var err error if param.precomputed { damgard, err = fullysec.NewDamgardPrecomp(l, param.modulusLength, bound) } else { damgard, err = fullysec.NewDamgard(l, param.modulusLength, bound) } if err != nil { t.Fatalf("Error during simple inner product creation: %v", err) } masterSecKey, masterPubKey, err := damgard.GenerateMasterKeys() if err != nil { t.Fatalf("Error during master key generation: %v", err) } y, err := data.NewRandomVector(l, sampler) if err != nil { t.Fatalf("Error during random generation: %v", err) } key, err := damgard.DeriveKey(masterSecKey, y) if err != nil { t.Fatalf("Error during key derivation: %v", err) } x, err := data.NewRandomVector(l, sampler) if err != nil { t.Fatalf("Error during random generation: %v", err) } // simulate the instantiation of encryptor (which should be given masterPubKey) encryptor := fullysec.NewDamgardFromParams(damgard.Params) xyCheck, err := x.Dot(y) if err != nil { t.Fatalf("Error during inner product calculation") } ciphertext, err := encryptor.Encrypt(x, masterPubKey) if err != nil { t.Fatalf("Error during encryption: %v", err) } decryptor := fullysec.NewDamgardFromParams(damgard.Params) xy, err := decryptor.Decrypt(ciphertext, key, y) if err != nil { t.Fatalf("Error during decryption: %v", err) } assert.Equal(t, xy.Cmp(xyCheck), 0, "obtained incorrect inner product") } func TestFullySec_DamgardDDH(t *testing.T) { params := []damgardTestParam{{name: "random", modulusLength: 512, precomputed: false}, {name: "precomputed", modulusLength: 2048, precomputed: true}} for _, param := range params { t.Run(param.name, func(t *testing.T) { testFullySecDamgardDDHFromParam(t, param) }) } } ================================================ FILE: innerprod/fullysec/dmcfe.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package fullysec import ( "fmt" "math/big" "crypto/sha256" "strconv" "github.com/fentec-project/bn256" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/internal/dlog" "github.com/fentec-project/gofe/sample" ) // DMCFEClient is to be instantiated by the client. Idx presents index of the client. type DMCFEClient struct { Idx int ClientSecKey *big.Int ClientPubKey *bn256.G1 Share data.Matrix S data.Vector } // NewDMCFEClient is to be called by the party that wants to encrypt number x_i. // The decryptor will be able to compute inner product of x and y where x = (x_1,...,x_l) and // y is publicly known vector y = (y_1,...,y_l). Value idx presents index of the party, where // it is assumed that if there are n clients, its indexes are in [0, n-1] func NewDMCFEClient(idx int) (*DMCFEClient, error) { sampler := sample.NewUniform(bn256.Order) s, err := data.NewRandomVector(2, sampler) if err != nil { return nil, fmt.Errorf("could not generate random vector") } sec, err := sampler.Sample() if err != nil { return nil, fmt.Errorf("could not generate random value") } pub := new(bn256.G1).ScalarBaseMult(sec) return &DMCFEClient{ Idx: idx, ClientSecKey: sec, ClientPubKey: pub, S: s, }, nil } // SetShare sets a shared key for client c, based on the public keys of all the // clients involved in the scheme. It assumes that Idx of a client indicates // which is the corresponding public key in pubKeys. Shared keys are such that // each client has a random key but all the shared keys sum to 0. func (c *DMCFEClient) SetShare(pubKeys []*bn256.G1) error { c.Share = data.NewConstantMatrix(2, 2, big.NewInt(0)) var add data.Matrix var err error for k := 0; k < len(pubKeys); k++ { if k == c.Idx { continue } sharedG1 := new(bn256.G1).ScalarMult(pubKeys[k], c.ClientSecKey) sharedKey := sha256.Sum256([]byte(sharedG1.String())) add, err = data.NewRandomDetMatrix(2, 2, bn256.Order, &sharedKey) if err != nil { return err } if k < c.Idx { c.Share, err = c.Share.Add(add) if err != nil { return err } } else { c.Share, err = c.Share.Sub(add) if err != nil { return err } } c.Share = c.Share.Mod(bn256.Order) } return nil } // Encrypt encrypts number x under some label. func (c *DMCFEClient) Encrypt(x *big.Int, label string) (*bn256.G1, error) { cipher := new(bn256.G1).ScalarBaseMult(big.NewInt(0)) for i := 0; i < 2; i++ { hs, err := bn256.HashG1(strconv.Itoa(i) + " " + label) if err != nil { return nil, err } hs.ScalarMult(hs, c.S[i]) cipher.Add(cipher, hs) } pow := new(big.Int).Set(x) gx := new(bn256.G1).ScalarBaseMult(big.NewInt(1)) if pow.Sign() < 0 { pow.Neg(pow) gx.Neg(gx) } gx.ScalarMult(gx, pow) cipher.Add(cipher, gx) return cipher, nil } // DeriveKeyShare generates client's key share. Decryptor needs shares from all clients. func (c *DMCFEClient) DeriveKeyShare(y data.Vector) (data.VectorG2, error) { hs := make([]*bn256.G2, 2) var err error for i := 0; i < 2; i++ { hs[i], err = bn256.HashG2(strconv.Itoa(i) + " " + y.String()) if err != nil { return nil, err } } keyShare := data.VectorG2{new(bn256.G2).ScalarBaseMult(big.NewInt(0)), new(bn256.G2).ScalarBaseMult(big.NewInt(0))} for k := 0; k < 2; k++ { for i := 0; i < 2; i++ { add := new(bn256.G2).ScalarMult(hs[i], c.Share[k][i]) keyShare[k].Add(keyShare[k], add) } pow := new(big.Int).Mul(y[c.Idx], c.S[k]) pow.Mod(pow, bn256.Order) gS := new(bn256.G2).ScalarBaseMult(pow) keyShare[k].Add(keyShare[k], gS) } return keyShare, nil } // DMCFEDecrypt is to be called by a party that wants to decrypt a message - to compute inner product // of x and y. It needs ciphertexts from all clients and key shares from all clients. The label is // a string under which vector x has been encrypted (each client encrypted x_i under this label). The value bound // specifies the bound of the output (solution will be in the interval (-bound, bound)) and can be nil. func DMCFEDecrypt(ciphers []*bn256.G1, keyShares []data.VectorG2, y data.Vector, label string, bound *big.Int) (*big.Int, error) { key1 := new(bn256.G2).ScalarBaseMult(big.NewInt(0)) key2 := new(bn256.G2).ScalarBaseMult(big.NewInt(0)) for i := 0; i < len(keyShares); i++ { key1.Add(key1, keyShares[i][0]) key2.Add(key2, keyShares[i][1]) } gen2 := new(bn256.G2).ScalarBaseMult(big.NewInt(1)) cSum := new(bn256.G1).ScalarBaseMult(big.NewInt(0)) cAdd := new(bn256.G1) pow := new(big.Int) for i := 0; i < len(ciphers); i++ { cAdd.Set(ciphers[i]) pow.Set(y[i]) if pow.Sign() < 0 { cAdd.Neg(cAdd) pow.Neg(pow) } cAdd.ScalarMult(cAdd, pow) cSum.Add(cSum, cAdd) } s := bn256.Pair(cSum, gen2) hs := make([]*bn256.G1, 2) var err error for i := 0; i < 2; i++ { hs[i], err = bn256.HashG1(strconv.Itoa(i) + " " + label) if err != nil { return nil, err } } t1 := bn256.Pair(hs[0], key1) t2 := bn256.Pair(hs[1], key2) t1.Add(t1, t2) t1.Neg(t1) s.Add(s, t1) g1gen := new(bn256.G1).ScalarBaseMult(big.NewInt(1)) g2gen := new(bn256.G2).ScalarBaseMult(big.NewInt(1)) g := bn256.Pair(g1gen, g2gen) dec, err := dlog.NewCalc().InBN256().WithNeg().WithBound(bound).BabyStepGiantStep(s, g) return dec, err } ================================================ FILE: innerprod/fullysec/dmcfe_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package fullysec_test import ( "math/big" "testing" "github.com/fentec-project/bn256" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/innerprod/fullysec" "github.com/fentec-project/gofe/sample" "github.com/pkg/errors" "github.com/stretchr/testify/assert" ) func Test_DMCFE(t *testing.T) { numClients := 10 clients := make([]*fullysec.DMCFEClient, numClients) pubKeys := make([]*bn256.G1, numClients) // create clients and make a slice of their public values for i := 0; i < numClients; i++ { c, err := fullysec.NewDMCFEClient(i) if err != nil { t.Fatalf("could not instantiate fullysec.Client: %v", err) } clients[i] = c pubKeys[i] = c.ClientPubKey } // based on public values of each client create private matrices T_i summing to 0 for i := 0; i < numClients; i++ { err := clients[i].SetShare(pubKeys) if err != nil { panic(errors.Wrap(err, "could not create private values")) } } // now that the clients have agreed on secret keys they can encrypt a vector in // a decentralized way and create partial keys such that only with all of them // the decryption of the inner product is possible label := "some label" bound := big.NewInt(1000) sampler := sample.NewUniformRange(new(big.Int).Add(new(big.Int).Neg(bound), big.NewInt(1)), bound) y, err := data.NewRandomVector(numClients, sampler) if err != nil { t.Fatalf("could not create random vector: %v", err) } x, err := data.NewRandomVector(numClients, sampler) if err != nil { t.Fatalf("could not create random vector: %v", err) } xy, err := x.Dot(y) if err != nil { t.Fatalf("could not compute inner product: %v", err) } ciphers := make([]*bn256.G1, numClients) keyShares := make([]data.VectorG2, numClients) for i := 0; i < numClients; i++ { c, err := clients[i].Encrypt(x[i], label) if err != nil { t.Fatalf("could not encrypt: %v", err) } ciphers[i] = c keyShare, err := clients[i].DeriveKeyShare(y) if err != nil { t.Fatalf("could not generate key share: %v", err) } keyShares[i] = keyShare } bound.Mul(bound, bound) bound.Mul(bound, big.NewInt(int64(numClients))) // numClients * (coordinate_bound)^2 d, err := fullysec.DMCFEDecrypt(ciphers, keyShares, y, label, bound) if err != nil { t.Fatalf("error when decrypting: %v", err) } assert.Equal(t, d, xy, "Decryption wrong") } ================================================ FILE: innerprod/fullysec/doc.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ // Package fullysec includes fully secure schemes for functional encryption // of inner products. // // All implementations in this package are based on the reference // paper by Agrawal, Libert and Stehlé (see https://eprint.iacr.org/2015/608.pdf), // and offer adaptive security under chosen-plaintext // attacks (IND-CPA security). // // The reference scheme is public key, which means that no master secret // key is required for the encryption. // // For instantiation from the decisional Diffie-Hellman assumption // (DDH), see struct Damgard (and its multi-input variant DamgardMulti, // which is a secret key scheme, because a part of the secret key is // required for the encryption). // // For instantiation from learning with errors (LWE), see // struct LWE. package fullysec ================================================ FILE: innerprod/fullysec/fh_multi_ipe.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package fullysec import ( "math/big" "github.com/fentec-project/bn256" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/internal/dlog" "github.com/fentec-project/gofe/sample" ) // FHMultiIPEParams represents configuration parameters for the FHMultiIPE // scheme instance. // SecLevel (int): The parameter defines the security assumption of the scheme, // so called k-Lin assumption, where k is the specified SecLevel. // NumClients (int): The number of clients participating // VecLen (int): The length of vectors that clients encrypt. // BoundX (int): The value by which the coordinates of encrypted vectors are bounded. // BoundY (int): The value by which the coordinates of inner product vectors are bounded. type FHMultiIPEParams struct { SecLevel int NumClients int VecLen int BoundX *big.Int BoundY *big.Int } // FHMultiIPE represents a Function Hiding Multi-input Inner Product // Encryption scheme based on the paper by P. Datta, T. Okamoto, and // J. Tomida: // "Full-Hiding (Unbounded) Multi-Input Inner Product Functional Encryption // from the 𝒌-Linear Assumption". // It allows clients to encrypt vectors {x_1,...,x_m} and derive a secret key // based on an inner product vectors {y_1,...,y_m} so that a decryptor can // decrypt the sum of inner products + ... + without // revealing vectors x_i or y_i. The scheme is slightly modified from the // original one to achieve a better performance. The difference is in // storing the secret master key as matrices B, BStar, instead of matrices // of elliptic curve elements g_1^B, g_2^BStar. This replaces elliptic curves // operations with matrix multiplication. // // This struct contains the shared choice for parameters on which the // functionality of the scheme depend. type FHMultiIPE struct { Params *FHMultiIPEParams } // FHMultiIPESecKey represents a master secret key in FHMultiIPE scheme. type FHMultiIPESecKey struct { BHat []data.Matrix BStarHat []data.Matrix } // NewFHMultiIPE configures a new instance of the scheme. See struct // FHMultiIPEParams for the description of the parameters. It returns // a new FHMultiIPE instance. func NewFHMultiIPE(secLevel, numClients, vecLen int, boundX, boundY *big.Int) *FHMultiIPE { params := &FHMultiIPEParams{SecLevel: secLevel, NumClients: numClients, VecLen: vecLen, BoundX: boundX, BoundY: boundY} return &FHMultiIPE{Params: params} } // NewFHMultiIPEFromParams takes configuration parameters of an existing // FHMultiIPE scheme instance, and reconstructs the scheme with the same // configuration parameters. It returns a new FHMultiIPE instance. func NewFHMultiIPEFromParams(params *FHMultiIPEParams) *FHMultiIPE { return &FHMultiIPE{ Params: params, } } // GenerateKeys generates a pair of master secret key and public key // for the scheme. It returns an error in case keys could not be // generated. func (f FHMultiIPE) GenerateKeys() (*FHMultiIPESecKey, *bn256.GT, error) { sampler := sample.NewUniformRange(big.NewInt(1), bn256.Order) mu, err := sampler.Sample() if err != nil { return nil, nil, err } gTMu := new(bn256.GT).ScalarBaseMult(mu) B := make([]data.Matrix, f.Params.NumClients) BStar := make([]data.Matrix, f.Params.NumClients) for i := 0; i < f.Params.NumClients; i++ { B[i], BStar[i], err = randomOB(2*f.Params.VecLen+2*f.Params.SecLevel+1, mu) if err != nil { return nil, nil, err } } BHat := make([]data.Matrix, f.Params.NumClients) BStarHat := make([]data.Matrix, f.Params.NumClients) for i := 0; i < f.Params.NumClients; i++ { BHat[i] = make(data.Matrix, f.Params.VecLen+f.Params.SecLevel+1) BStarHat[i] = make(data.Matrix, f.Params.VecLen+f.Params.SecLevel) for j := 0; j < f.Params.VecLen+f.Params.SecLevel+1; j++ { if j < f.Params.VecLen { BHat[i][j] = B[i][j] BStarHat[i][j] = BStar[i][j] } else if j == f.Params.VecLen { BHat[i][j] = B[i][j+f.Params.VecLen] BStarHat[i][j] = BStar[i][j+f.Params.VecLen] } else if j < f.Params.VecLen+f.Params.SecLevel { BHat[i][j] = B[i][j-1+f.Params.VecLen+f.Params.SecLevel] BStarHat[i][j] = BStar[i][j+f.Params.VecLen] } else { BHat[i][j] = B[i][j-1+f.Params.VecLen+f.Params.SecLevel] } } } return &FHMultiIPESecKey{BHat: BHat, BStarHat: BStarHat}, gTMu, nil } // randomOB is a helping function that samples a random l x l matrix B // and calculates BStar = mu * (B^-1)^T func randomOB(l int, mu *big.Int) (data.Matrix, data.Matrix, error) { sampler := sample.NewUniform(bn256.Order) B, err := data.NewRandomMatrix(l, l, sampler) if err != nil { return nil, nil, err } BStar, _, err := B.InverseModGauss(bn256.Order) if err != nil { return nil, nil, err } BStar = BStar.Transpose() BStar = BStar.MulScalar(mu) BStar = BStar.Mod(bn256.Order) return B, BStar, nil } // DeriveKey takes a matrix y whose rows are input vector y_1,...,y_m and // master secret key, and returns the functional encryption key. That is // a key that for encrypted x_1,...,x_m allows to calculate the sum of // inner products + ... + . In case the key could not // be derived, it returns an error. func (f FHMultiIPE) DeriveKey(y data.Matrix, secKey *FHMultiIPESecKey) (data.MatrixG2, error) { sampler := sample.NewUniform(bn256.Order) gamma, err := data.NewRandomMatrix(f.Params.SecLevel, f.Params.NumClients, sampler) if err != nil { return nil, err } ones := data.NewConstantVector(f.Params.NumClients-1, big.NewInt(1)) r := data.NewVector(gamma[0][0:(f.Params.NumClients - 1)]) sum, err := r.Dot(ones) if err != nil { return nil, err } sum.Neg(sum).Mod(sum, bn256.Order) gamma[0][f.Params.NumClients-1] = sum zeros := data.NewConstantVector(2*f.Params.VecLen+2*f.Params.SecLevel+1, big.NewInt(0)) keyMat := make(data.Matrix, f.Params.NumClients) var s *big.Int for i := 0; i < f.Params.NumClients; i++ { keyMat[i] = zeros.Copy() for j := 0; j < f.Params.VecLen+f.Params.SecLevel; j++ { if j < f.Params.VecLen { s = y[i][j] } else { s = gamma[j-f.Params.VecLen][i] } keyMat[i] = keyMat[i].Add(secKey.BStarHat[i][j].MulScalar(s)) keyMat[i] = keyMat[i].Mod(bn256.Order) } } return keyMat.MulG2(), nil } // Encrypt encrypts input vector x with the provided part of the master secret key. // It returns a ciphertext vector. If encryption failed, error is returned. func (f FHMultiIPE) Encrypt(x data.Vector, partSecKey data.Matrix) (data.VectorG1, error) { sampler := sample.NewUniform(bn256.Order) phi, err := data.NewRandomVector(f.Params.SecLevel, sampler) if err != nil { return nil, err } keyVec := data.NewConstantVector(2*f.Params.VecLen+2*f.Params.SecLevel+1, big.NewInt(0)) var s *big.Int for j := 0; j < f.Params.VecLen+f.Params.SecLevel+1; j++ { if j < f.Params.VecLen { s = x[j] } else if j == f.Params.VecLen { s = big.NewInt(1) } else { s = phi[j-f.Params.VecLen-1] } keyVec = keyVec.Add(partSecKey[j].MulScalar(s)) keyVec = keyVec.Mod(bn256.Order) } return keyVec.MulG1(), nil } // Decrypt accepts the ciphertext as a matrix whose rows are encryptions of vectors // x_1,...,x_m and a functional encryption key corresponding to vectors y_1,...,y_m. // It returns the sum of inner products + ... + . If decryption // failed, an error is returned. func (f *FHMultiIPE) Decrypt(cipher data.MatrixG1, key data.MatrixG2, pubKey *bn256.GT) (*big.Int, error) { sum := new(bn256.GT).ScalarBaseMult(big.NewInt(0)) for i := 0; i < f.Params.NumClients; i++ { for j := 0; j < 2*f.Params.VecLen+2*f.Params.SecLevel+1; j++ { paired := bn256.Pair(cipher[i][j], key[i][j]) sum.Add(paired, sum) } } boundXY := new(big.Int).Mul(f.Params.BoundX, f.Params.BoundY) bound := new(big.Int).Mul(big.NewInt(int64(f.Params.NumClients*f.Params.VecLen)), boundXY) dec, err := dlog.NewCalc().InBN256().WithNeg().WithBound(bound).BabyStepGiantStep(sum, pubKey) return dec, err } ================================================ FILE: innerprod/fullysec/fh_multi_ipe_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package fullysec_test import ( "math/big" "testing" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/innerprod/fullysec" "github.com/fentec-project/gofe/sample" "github.com/stretchr/testify/assert" ) func TestFH_Multi_IPE(t *testing.T) { // choose the parameters for the scheme secLevel := 2 vecLen := 10 numClient := 5 bound := big.NewInt(128) // build the scheme fhmulti := fullysec.NewFHMultiIPE(secLevel, numClient, vecLen, bound, bound) // generate master secret key and public key masterSecKey, pubKey, err := fhmulti.GenerateKeys() if err != nil { t.Fatalf("Error during keys generation: %v", err) } // sample vectors that will be encrypted sampler := sample.NewUniformRange(new(big.Int).Add(new(big.Int).Neg(bound), big.NewInt(1)), bound) x := make(data.Matrix, numClient) for i := 0; i < numClient; i++ { x[i], err = data.NewRandomVector(vecLen, sampler) if err != nil { t.Fatalf("Error during random vector generation: %v", err) } } // simulate different clients (encryptors which should be given a part of the master key) // and encrypt their vectors cipher := make(data.MatrixG1, numClient) clients := make([]*fullysec.FHMultiIPE, numClient) for i := 0; i < numClient; i++ { clients[i] = fullysec.NewFHMultiIPEFromParams(fhmulti.Params) cipher[i], err = clients[i].Encrypt(x[i], masterSecKey.BHat[i]) if err != nil { t.Fatalf("Error during encryption: %v", err) } } // sample inner product vectors and put them in a matrix y := make(data.Matrix, numClient) for i := 0; i < numClient; i++ { y[i], err = data.NewRandomVector(vecLen, sampler) if err != nil { t.Fatalf("Error during random vector generation: %v", err) } } // derive a functional key for vector y key, err := fhmulti.DeriveKey(y, masterSecKey) if err != nil { t.Fatalf("Error during key derivation: %v", err) } // simulate a decryptor decryptor := fullysec.NewFHMultiIPEFromParams(fhmulti.Params) // decryptor decrypts the inner-product without knowing // vectors x and y xy, err := decryptor.Decrypt(cipher, key, pubKey) if err != nil { t.Fatalf("Error during decryption: %v", err) } // check the correctness of the result xyCheck, err := x.Dot(y) if err != nil { t.Fatalf("Error during inner product calculation") } assert.Equal(t, xy.Cmp(xyCheck), 0, "obtained incorrect inner product") } ================================================ FILE: innerprod/fullysec/fhipe.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package fullysec import ( "fmt" "math/big" "crypto/rand" "github.com/fentec-project/bn256" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/internal/dlog" "github.com/fentec-project/gofe/sample" ) // FHIPEParams holds common parameters used in the scheme. These are: // L (int): The length of vectors to be encrypted. // BoundX (int): The value by which coordinates of encrypted vectors x are bounded. // BoundY (int): The value by which coordinates of inner product vectors y are bounded. type FHIPEParams struct { L int BoundX *big.Int BoundY *big.Int } // FHIPE represents a Function Hiding Inner Product Encryption scheme // based on the paper by Kim, Lewi, Mandal, Montgomery, Roy, Wu: // "Function-Hiding Inner Product Encryption is Practical". // It allows to encrypt a vector x and derive a secret key based // on an inner product vector y so that a deryptor can decrypt the // inner product without revealing x or y. // The struct contains the shared choice for parameters on which // the functionality of the scheme depend. type FHIPE struct { Params *FHIPEParams } // NewFHIPE configures a new instance of the scheme. // It accepts the length of input vectors l, a bound by which // the coordinates of encryption vectors are bounded, and similarly a // bound by which the coordinates of inner product vectors are bounded. // // It returns an error in case the scheme could not be properly // configured, or if the possible decryption value is to big. func NewFHIPE(l int, boundX, boundY *big.Int) (*FHIPE, error) { boundXY := new(big.Int).Mul(boundX, boundY) prod := new(big.Int).Mul(big.NewInt(int64(2*l)), boundXY) if prod.Cmp(bn256.Order) > 0 { return nil, fmt.Errorf("2 * l * boundX * boundY should be smaller than group order") } return &FHIPE{ Params: &FHIPEParams{ L: l, BoundX: boundX, BoundY: boundY}}, nil } // NewFHIPEFromParams takes configuration parameters of an existing // FHIPE scheme instance, and reconstructs the scheme with same configuration // parameters. It returns a new FHIPE instance. func NewFHIPEFromParams(params *FHIPEParams) *FHIPE { return &FHIPE{ Params: params, } } // FHIPESecKey is a secret key for FHIPE scheme. type FHIPESecKey struct { G1 *bn256.G1 G2 *bn256.G2 B data.Matrix BStar data.Matrix } // GenerateMasterKey generates a master secret key for the scheme. // It returns an error in case master key could not be generated. func (d *FHIPE) GenerateMasterKey() (*FHIPESecKey, error) { _, g1, err := bn256.RandomG1(rand.Reader) if err != nil { return nil, err } _, g2, err := bn256.RandomG2(rand.Reader) if err != nil { return nil, err } sampler := sample.NewUniform(bn256.Order) b, err := data.NewRandomMatrix(d.Params.L, d.Params.L, sampler) if err != nil { return nil, err } bStar, det, err := b.InverseModGauss(bn256.Order) if err != nil { return nil, err } bStar = bStar.Transpose() bStar = bStar.MulScalar(det) bStar = bStar.Mod(bn256.Order) return &FHIPESecKey{G1: g1, G2: g2, B: b, BStar: bStar}, nil } // FHIPEDerivedKey is a functional encryption key for FHIPE scheme. type FHIPEDerivedKey struct { K1 *bn256.G1 K2 data.VectorG1 } // DeriveKey takes a master key and input vector y, and returns a // functional encryption key. In case the key could not be derived, it // returns an error. func (d *FHIPE) DeriveKey(y data.Vector, masterKey *FHIPESecKey) (*FHIPEDerivedKey, error) { if err := y.CheckBound(d.Params.BoundY); err != nil { return nil, err } if len(y) != d.Params.L { return nil, fmt.Errorf("vector dimension error") } if masterKey.B.Rows() != d.Params.L { return nil, fmt.Errorf("master key dimensions error") } sampler := sample.NewUniform(bn256.Order) alpha, err := sampler.Sample() if err != nil { return nil, err } det, err := masterKey.B.DeterminantGauss(bn256.Order) if err != nil { return nil, err } k1 := new(bn256.G1).ScalarMult(masterKey.G1, det) k1.ScalarMult(k1, alpha) alphaBY, err := masterKey.B.MulVec(y) if err != nil { return nil, err } alphaBY = alphaBY.MulScalar(alpha) alphaBY = alphaBY.Mod(bn256.Order) g1Vec := make(data.VectorG1, d.Params.L) for i := 0; i < d.Params.L; i++ { g1Vec[i] = new(bn256.G1).Set(masterKey.G1) } k2 := alphaBY.MulVecG1(g1Vec) return &FHIPEDerivedKey{K1: k1, K2: k2}, nil } // FHIPECipher is a functional encryption ciphertext for FHIPE scheme. type FHIPECipher struct { C1 *bn256.G2 C2 data.VectorG2 } // Encrypt encrypts input vector x with the provided master key and returns a ciphertext. // If encryption failed, error is returned. func (d *FHIPE) Encrypt(x data.Vector, masterKey *FHIPESecKey) (*FHIPECipher, error) { if err := x.CheckBound(d.Params.BoundX); err != nil { return nil, err } if len(x) != d.Params.L { return nil, fmt.Errorf("vector dimension error") } if masterKey.B.Rows() != d.Params.L { return nil, fmt.Errorf("master key dimensions error") } sampler := sample.NewUniform(bn256.Order) beta, err := sampler.Sample() if err != nil { return nil, err } c1 := new(bn256.G2).ScalarMult(masterKey.G2, beta) betaBStarX, err := masterKey.BStar.MulVec(x) if err != nil { return nil, err } betaBStarX = betaBStarX.MulScalar(beta) betaBStarX = betaBStarX.Mod(bn256.Order) g2Vec := make(data.VectorG2, d.Params.L) for i := 0; i < d.Params.L; i++ { g2Vec[i] = new(bn256.G2).Set(masterKey.G2) } c2 := betaBStarX.MulVecG2(g2Vec) return &FHIPECipher{C1: c1, C2: c2}, nil } // Decrypt accepts the ciphertext and functional encryption key. // It returns the inner product of x and y. If decryption failed, // an error is returned. func (d *FHIPE) Decrypt(cipher *FHIPECipher, key *FHIPEDerivedKey) (*big.Int, error) { if len(cipher.C2) != d.Params.L || len(key.K2) != d.Params.L { return nil, fmt.Errorf("key or cipher length error") } d1 := bn256.Pair(key.K1, cipher.C1) d2 := new(bn256.GT).ScalarBaseMult(big.NewInt(0)) for i := 0; i < d.Params.L; i++ { pairedI := bn256.Pair(key.K2[i], cipher.C2[i]) d2 = new(bn256.GT).Add(pairedI, d2) } // calculate the upper bound of the result needed for the // discrete logarithm computation boundXY := new(big.Int).Mul(d.Params.BoundX, d.Params.BoundY) bound := new(big.Int).Mul(big.NewInt(int64(d.Params.L)), boundXY) dec, err := dlog.NewCalc().InBN256().WithNeg().WithBound(bound).BabyStepGiantStep(d2, d1) return dec, err } ================================================ FILE: innerprod/fullysec/fhipe_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package fullysec_test import ( "math/big" "testing" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/innerprod/fullysec" "github.com/fentec-project/gofe/sample" "github.com/stretchr/testify/assert" ) func TestFHIPE(t *testing.T) { // choose the parameters for the encryption and build the scheme l := 30 bound := big.NewInt(128) fhipe, err := fullysec.NewFHIPE(l, bound, bound) if err != nil { t.Fatalf("Error during scheme creation: %v", err) } // generate master key masterSecKey, err := fhipe.GenerateMasterKey() if err != nil { t.Fatalf("Error during master key generation: %v", err) } // sample a vector that will be encrypted sampler := sample.NewUniformRange(new(big.Int).Add(new(big.Int).Neg(bound), big.NewInt(1)), bound) x, err := data.NewRandomVector(l, sampler) if err != nil { t.Fatalf("Error during random vector generation: %v", err) } // simulate the instantiation of an encryptor (which should know the master key) encryptor := fullysec.NewFHIPEFromParams(fhipe.Params) // encrypt the vector ciphertext, err := encryptor.Encrypt(x, masterSecKey) if err != nil { t.Fatalf("Error during encryption: %v", err) } // sample a inner product vector y, err := data.NewRandomVector(l, sampler) if err != nil { t.Fatalf("Error during random vecotr generation: %v", err) } // derive a functional key for vector y key, err := fhipe.DeriveKey(y, masterSecKey) if err != nil { t.Fatalf("Error during key derivation: %v", err) } // simulate a decryptor decryptor := fullysec.NewFHIPEFromParams(fhipe.Params) // decryptor decrypts the inner-product without knowing // vectors x and y xy, err := decryptor.Decrypt(ciphertext, key) if err != nil { t.Fatalf("Error during decryption: %v", err) } // check the correctness of the result xyCheck, err := x.Dot(y) if err != nil { t.Fatalf("Error during inner product calculation") } assert.Equal(t, xy.Cmp(xyCheck), 0, "obtained incorrect inner product") } ================================================ FILE: innerprod/fullysec/lwe.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package fullysec import ( "crypto/rand" "math" "math/big" "github.com/fentec-project/gofe/data" gofe "github.com/fentec-project/gofe/internal" "github.com/fentec-project/gofe/sample" "github.com/pkg/errors" ) // LWEParams represents parameters for the fully secure LWE scheme. type LWEParams struct { L int // Length of data vectors for inner product N int // Main security parameters of the scheme M int // Number of samples BoundX *big.Int // Message space size BoundY *big.Int // Inner product vector space size // Modulus for the resulting inner product. // K depends on the parameters L, P and V and is computed by the scheme. K *big.Int // Modulus for ciphertext and keys. // Must be significantly larger than K. Q *big.Int // standard deviation for the noise terms in the encryption process SigmaQ *big.Float // precomputed LSigmaQ = SigmaQ / (1/2log(2)) needed for sampling LSigmaQ *big.Int // standard deviation for first half of the matrix for sampling private key Sigma1 *big.Float // precomputed LSigma1 = Sigma1 / (1/2log(2)) needed for sampling LSigma1 *big.Int // standard deviation for second half of the matrix for sampling private key Sigma2 *big.Float // precomputed LSigma2 = Sigma2 / (1/2log(2)) needed for sampling LSigma2 *big.Int // Matrix A of dimensions M*N is a public parameter of the scheme A data.Matrix } // LWE represents a scheme instantiated from the LWE problem. // Based on the LWE variant of: // Agrawal, Shweta, Libert, and Stehle: // "Fully secure functional encryption for inner products, // from standard assumptions". type LWE struct { Params *LWEParams } // NewLWE configures a new instance of the scheme. // It accepts the length of input vectors l, the main security parameter // n, the message space size boundX, and the inner product vector space size // boundY. The function sets up the remaining public parameters as // it is suggested in the paper by Agrawal, Shweta, Libert, and Stehle: // "Fully secure functional encryption for inner products, // from standard assumptions". // Note that this is a prototype implementation and should not be // used in production before security testing against various // known attacks has been performed. Unfortunately, no such (theoretical) // evaluation exists yet in the literature. // // It returns an error in case public parameters of the scheme could // not be generated. func NewLWE(l, n int, boundX, boundY *big.Int) (*LWE, error) { // K = 2 * l * boundX * boundY K := new(big.Int).Mul(boundX, boundY) K.Mul(K, big.NewInt(int64(l*2))) kF := new(big.Float).SetInt(K) SquaredF := new(big.Float).Mul(kF, kF) nF := float64(n) nBitsQ := 1 var sigma, sigma1, sigma2 *big.Float var lSigma1, lSigma2 *big.Int // parameters for the scheme are given as a set of requirements in the paper // hence we search for such parameters iteratively for i := 1; true; i++ { //assuming that the final q will have at most i bits we calculate a bound boundMF := float64(n * i) // tmp values log2M := math.Log2(boundMF) sqrtNLogM := math.Sqrt(nF * log2M) max := new(big.Float) if SquaredF.Cmp(big.NewFloat(boundMF)) == 1 { max.SetFloat64(boundMF) } else { max.Set(SquaredF) } sqrtMax := new(big.Float).Sqrt(max) sigma1 = new(big.Float).Mul(big.NewFloat(sqrtNLogM), sqrtMax) // to sample with NormalDoubleConstant sigmaQ must be // a multiple of sample.SigmaCDT = sqrt(1/2ln(2)), hence we make // it such lSigma1F := new(big.Float).Quo(sigma1, sample.SigmaCDT) lSigma1, _ = lSigma1F.Int(nil) sigma1.Mul(sample.SigmaCDT, lSigma1F) // tmp values nPow3 := math.Pow(nF, 3) powSqrtLogM5 := math.Pow(math.Sqrt(log2M), 5) mulVal := math.Sqrt(nF) * nPow3 * powSqrtLogM5 * math.Sqrt(boundMF) sigma2 = new(big.Float).Mul(big.NewFloat(mulVal), max) // to sample with NormalDoubleConstant sigmaQ must be // a multiple of sample.SigmaCDT = sqrt(1/2ln(2)), hence we make // it such lSigma2F := new(big.Float).Quo(sigma2, sample.SigmaCDT) lSigma2, _ = lSigma2F.Int(nil) sigma2.Mul(sample.SigmaCDT, lSigma2F) // tmp value sigma1Square := new(big.Float).Mul(sigma1, sigma1) sigma2Square := new(big.Float).Mul(sigma2, sigma2) bound2 := new(big.Float).Add(sigma1Square, sigma2Square) bound2.Sqrt(bound2) bound2.Mul(bound2, big.NewFloat(math.Sqrt(nF))) sigma = new(big.Float).Quo(big.NewFloat(1), SquaredF) sigma.Quo(sigma, bound2) sigma.Quo(sigma, big.NewFloat(math.Log2(nF))) // assuming number of bits of q will be at least nBitsQ from the previous // iteration (this is always true) we calculate sigma prime nfPow6 := math.Pow(nF, 6) nBitsQPow2 := math.Pow(float64(nBitsQ), 2) sqrtLog2nFPow5 := math.Pow(math.Sqrt(math.Log2(nF)), 5) sigmaPrime := new(big.Float).Quo(sigma, kF) sigmaPrime.Quo(sigmaPrime, big.NewFloat(nfPow6*nBitsQPow2*sqrtLog2nFPow5)) boundForQ := new(big.Float) boundForQ.Quo(big.NewFloat(math.Sqrt(math.Log2(nF))), sigmaPrime) nBitsQ = boundForQ.MantExp(nil) + 1 // check if the number of bits for q is greater than i as it was // assumed at the beginning of the iteration if nBitsQ < i { break } // in the next iteration the number of bits for q must be at least as // many as it was demanded in this iteration i = nBitsQ } // get q q, err := rand.Prime(rand.Reader, nBitsQ) if err != nil { return nil, err } m := int(1.01 * nF * float64(nBitsQ)) // get sigmaQ qF := new(big.Float).SetInt(q) sigmaQ := new(big.Float).Mul(sigma, qF) // to sample with NormalDoubleConstant sigmaQ must be // a multiple of sample.SigmaCDT = sqrt(1/2ln(2)), hence we make // it such lSigmaQF := new(big.Float).Quo(sigmaQ, sample.SigmaCDT) lSigmaQ, _ := lSigmaQF.Int(nil) sigmaQ.Mul(sample.SigmaCDT, lSigmaQF) randMat, err := data.NewRandomMatrix(m, n, sample.NewUniform(q)) if err != nil { return nil, err } return &LWE{ Params: &LWEParams{ L: l, N: n, M: m, BoundX: boundX, BoundY: boundY, Q: q, K: K, SigmaQ: sigmaQ, LSigmaQ: lSigmaQ, Sigma1: sigma1, LSigma1: lSigma1, Sigma2: sigma2, LSigma2: lSigma2, A: randMat, }, }, nil } // GenerateSecretKey generates a secret key for the scheme. // The secret key is a matrix with dimensions l*m. // // In case secret key could not be generated, it returns an error. func (s *LWE) GenerateSecretKey() (data.Matrix, error) { var val *big.Int sampler1 := sample.NewNormalDoubleConstant(s.Params.LSigma1) sampler2 := sample.NewNormalDoubleConstant(s.Params.LSigma2) Z := make(data.Matrix, s.Params.L) halfRows := Z.Rows() / 2 for i := 0; i < Z.Rows(); i++ { Z[i] = make(data.Vector, s.Params.M) for j := 0; j < Z.Cols(); j++ { if j < halfRows { // first half val, _ = sampler1.Sample() } else { // second half val, _ = sampler2.Sample() if j-halfRows == i { val.Add(val, big.NewInt(1)) } } Z[i][j] = val } } return Z, nil } // GeneratePublicKey accepts a master secret key Z and generates a // corresponding master public key. // Public key is a matrix of l*m elements. // In case of a malformed secret key the function returns an error. func (s *LWE) GeneratePublicKey(Z data.Matrix) (data.Matrix, error) { if !Z.CheckDims(s.Params.L, s.Params.M) { return nil, gofe.ErrMalformedSecKey } // public key is obtained by multiplying secret key Z by a random matrix A. U, _ := Z.Mul(s.Params.A) U = U.Mod(s.Params.Q) return U, nil } // DeriveKey accepts input vector y and master secret key Z, and derives a // functional encryption key. // In case of malformed secret key or input vector that violates the // configured bound, it returns an error. func (s *LWE) DeriveKey(y data.Vector, Z data.Matrix) (data.Vector, error) { if err := y.CheckBound(s.Params.BoundY); err != nil { return nil, err } if !Z.CheckDims(s.Params.L, s.Params.M) { return nil, gofe.ErrMalformedSecKey } // Secret key is a linear combination of input vector x and master secret key Z. zY, err := Z.Transpose().MulVec(y) if err != nil { return nil, gofe.ErrMalformedInput } zY = zY.Mod(s.Params.Q) return zY, nil } // Encrypt encrypts vector y using public key U. // It returns the resulting ciphertext vector. In case of malformed // public key or input vector that violates the configured bound, // it returns an error. func (s *LWE) Encrypt(x data.Vector, U data.Matrix) (data.Vector, error) { if err := x.CheckBound(s.Params.BoundX); err != nil { return nil, err } if !U.CheckDims(s.Params.L, s.Params.N) { return nil, gofe.ErrMalformedPubKey } if len(x) != s.Params.L { return nil, gofe.ErrMalformedInput } // Create a random vector r, err := data.NewRandomVector(s.Params.N, sample.NewUniform(s.Params.Q)) if err != nil { return nil, errors.Wrap(err, "error in encrypt") } // calculate the standard distribution and sample vectors e0, e1 sampler := sample.NewNormalDoubleConstant(s.Params.LSigmaQ) e0, err0 := data.NewRandomVector(s.Params.M, sampler) e1, err1 := data.NewRandomVector(s.Params.L, sampler) if err0 != nil || err1 != nil { return nil, errors.Wrap(err0, "error in encrypt") } // calculate first part of the cipher c0, _ := s.Params.A.MulVec(r) c0 = c0.Add(e0) c0 = c0.Mod(s.Params.Q) // calculate second part of the cipher qDivK := new(big.Int).Div(s.Params.Q, s.Params.K) t := x.MulScalar(qDivK) // center c1, _ := U.MulVec(r) c1 = c1.Add(e1) c1 = c1.Add(t) c1 = c1.Mod(s.Params.Q) return append(c0, c1...), nil } // Decrypt accepts an encrypted vector cipher, functional encryption key zX, // and plaintext vector x, and calculates the inner product of x and y. // If decryption failed (for instance with input data that violates the // configured bound or malformed ciphertext or keys), error is returned. func (s *LWE) Decrypt(cipher, zY, y data.Vector) (*big.Int, error) { if err := y.CheckBound(s.Params.BoundY); err != nil { return nil, err } if len(zY) != s.Params.M { return nil, gofe.ErrMalformedDecKey } if len(y) != s.Params.L { return nil, gofe.ErrMalformedInput } if len(cipher) != s.Params.M+s.Params.L { return nil, gofe.ErrMalformedCipher } c0 := cipher[:s.Params.M] c1 := cipher[s.Params.M:] yDotC1, _ := y.Dot(c1) zYDotC0, _ := zY.Dot(c0) mu1 := new(big.Int).Sub(yDotC1, zYDotC0) mu1.Mod(mu1, s.Params.Q) if mu1.Cmp(new(big.Int).Quo(s.Params.Q, big.NewInt(2))) == 1 { mu1.Sub(mu1, s.Params.Q) } paramsKTimes2 := new(big.Int).Lsh(s.Params.K, 1) qDivK := new(big.Int).Div(s.Params.Q, s.Params.K) qDivKTimes2 := new(big.Int).Div(s.Params.Q, paramsKTimes2) mu := new(big.Int).Add(mu1, qDivKTimes2) mu.Div(mu, qDivK) return mu, nil } ================================================ FILE: innerprod/fullysec/lwe_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package fullysec_test import ( "math/big" "testing" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/innerprod/fullysec" "github.com/fentec-project/gofe/sample" "github.com/stretchr/testify/assert" ) func TestFullySec_LWE(t *testing.T) { l := 4 n := 64 boundX := big.NewInt(1000) // maximal size of the entry of the message boundY := big.NewInt(1000) // maximal size of the entry of the other operand for inner product x, y, xy := testVectorData(l, boundX, boundY) emptyVec := data.Vector{} emptyMat := data.Matrix{} fsLWE, err := fullysec.NewLWE(l, n, boundX, boundY) assert.NoError(t, err) Z, err := fsLWE.GenerateSecretKey() assert.NoError(t, err) _, err = fsLWE.GeneratePublicKey(emptyMat) assert.Error(t, err) U, err := fsLWE.GeneratePublicKey(Z) assert.NoError(t, err) _, err = fsLWE.DeriveKey(emptyVec, Z) assert.Error(t, err) _, err = fsLWE.DeriveKey(y, emptyMat) assert.Error(t, err) _, err = fsLWE.DeriveKey(y.MulScalar(big.NewInt(10000)), emptyMat) assert.Error(t, err) // boundary violation zY, err := fsLWE.DeriveKey(y, Z) assert.NoError(t, err) _, err = fsLWE.Encrypt(emptyVec, U) assert.Error(t, err) _, err = fsLWE.Encrypt(x, emptyMat) assert.Error(t, err) _, err = fsLWE.Encrypt(x.MulScalar(big.NewInt(10000)), U) assert.Error(t, err) // boundary violation cipher, err := fsLWE.Encrypt(x, U) assert.NoError(t, err) _, err = fsLWE.Decrypt(emptyVec, zY, y) assert.Error(t, err) _, err = fsLWE.Decrypt(cipher, emptyVec, y) assert.Error(t, err) _, err = fsLWE.Decrypt(cipher, zY, emptyVec) assert.Error(t, err) _, err = fsLWE.Decrypt(cipher, zY, y.MulScalar(big.NewInt(10000))) assert.Error(t, err) // boundary violation xyDecrypted, err := fsLWE.Decrypt(cipher, zY, y) assert.NoError(t, err) assert.Equal(t, xy.Cmp(xyDecrypted), 0, "obtained incorrect inner product") } // testVectorData returns random vectors x, y, each containing // elements up to the respective bound. // It also returns the dot product of the vectors. func testVectorData(len int, boundX, boundY *big.Int) (data.Vector, data.Vector, *big.Int) { samplerX := sample.NewUniformRange(new(big.Int).Neg(boundX), boundX) samplerY := sample.NewUniformRange(new(big.Int).Neg(boundY), boundY) x, _ := data.NewRandomVector(len, samplerX) y, _ := data.NewRandomVector(len, samplerY) xy, _ := x.Dot(y) return x, y, xy } ================================================ FILE: innerprod/fullysec/paillier.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package fullysec import ( "crypto/rand" "fmt" "math/big" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/internal" "github.com/fentec-project/gofe/internal/keygen" "github.com/fentec-project/gofe/sample" ) // PaillierParams represents parameters for the fully secure Paillier scheme. type PaillierParams struct { L int // Length of data vectors for inner product N *big.Int // a big integer, a product of two safe primes NSquare *big.Int // N^2 a modulus for computations BoundX *big.Int // a bound on the entries of the input vector BoundY *big.Int // a bound on the entries of the inner product vector Sigma *big.Float // the standard deviation for the sampling a secret key LSigma *big.Int // precomputed Sigma/(1/2log(2)) needed for sampling Lambda int // security parameter G *big.Int // generator of the 2n-th residues subgroup of Z_N^2* } // Paillier represents a scheme based on the Paillier variant by // Agrawal, Shweta, Libert, and Stehle": // "Fully secure functional encryption for inner products, // from standard assumptions". type Paillier struct { Params *PaillierParams } // NewPaillier configures a new instance of the scheme. // It accepts the length of input vectors l, security parameter lambda, // the bit length of prime numbers (giving security to the scheme, it // should be such that factoring two primes with such a bit length takes // at least 2^lambda operations), and boundX and boundY by which // coordinates of input vectors and inner product vectors are bounded. // If you are not sure how to choose lambda and bitLen, setting // lambda = 128, bitLen = 1024 will result in a scheme that is believed // to have 128 bit security. // // It returns an error in the case the scheme could not be properly // configured, or if the precondition boundX, boundY < (n / l)^(1/2) // is not satisfied. func NewPaillier(l, lambda, bitLen int, boundX, boundY *big.Int) (*Paillier, error) { // generate two safe primes p, err := keygen.GetSafePrime(bitLen) if err != nil { return nil, err } q, err := keygen.GetSafePrime(bitLen) if err != nil { return nil, err } // calculate n = p * q n := new(big.Int).Mul(p, q) // calculate n^2 nSquare := new(big.Int).Mul(n, n) // check if the parameters of the scheme are compatible, // i.e. security parameter should be big enough that // the generated n is much greater than l and the bounds if boundX != nil && boundY != nil { xSquareL := new(big.Int).Mul(boundX, boundX) xSquareL.Mul(xSquareL, big.NewInt(int64(2*l))) ySquareL := new(big.Int).Mul(boundY, boundY) ySquareL.Mul(ySquareL, big.NewInt(int64(2*l))) if n.Cmp(xSquareL) < 1 { return nil, fmt.Errorf("parameters generation failed," + "boundX and l too big for bitLen") } if n.Cmp(ySquareL) < 1 { return nil, fmt.Errorf("parameters generation failed," + "boundY and l too big for bitLen") } } // generate a generator for the 2n-th residues subgroup of Z_n^2* gPrime, err := rand.Int(rand.Reader, nSquare) if err != nil { return nil, err } g := new(big.Int).Exp(gPrime, n, nSquare) g.Exp(g, big.NewInt(2), nSquare) // check if generated g is invertible, which should be the case except with // negligible probability if check := new(big.Int).ModInverse(g, nSquare); check == nil { return nil, fmt.Errorf("parameters generation failed," + "unexpected event of generator g is not invertible") } // calculate sigma nTo5 := new(big.Int).Exp(n, big.NewInt(5), nil) sigma := new(big.Float).SetInt(nTo5) sigma.Mul(sigma, big.NewFloat(float64(lambda))) sigma.Sqrt(sigma) sigma.Add(sigma, big.NewFloat(2)) // to sample with NormalDoubleConstant sigma must be // a multiple of sample.SigmaCDT = sqrt(1/2ln(2)), hence we make // it such lSigmaF := new(big.Float).Quo(sigma, sample.SigmaCDT) lSigma, _ := lSigmaF.Int(nil) lSigma.Add(lSigma, big.NewInt(1)) sigma.Mul(sample.SigmaCDT, lSigmaF) return &Paillier{ Params: &PaillierParams{ L: l, N: n, NSquare: nSquare, BoundX: boundX, BoundY: boundY, Sigma: sigma, LSigma: lSigma, Lambda: lambda, G: g, }, }, nil } // NewPaillierFromParams takes configuration parameters of an existing // Paillier scheme instance, and reconstructs the scheme with same configuration // parameters. It returns a new Paillier instance. func NewPaillierFromParams(params *PaillierParams) *Paillier { return &Paillier{ Params: params, } } // GenerateMasterKeys generates a master secret key and a master // public key for the scheme. It returns an error in case master keys // could not be generated. func (s *Paillier) GenerateMasterKeys() (data.Vector, data.Vector, error) { // sampler for sampling a secret key sampler := sample.NewNormalDoubleConstant(s.Params.LSigma) // generate a secret key secKey, err := data.NewRandomVector(s.Params.L, sampler) if err != nil { return nil, nil, err } // derive the public key from the generated secret key pubKey := secKey.Apply(func(x *big.Int) *big.Int { return internal.ModExp(s.Params.G, x, s.Params.NSquare) }) return secKey, pubKey, nil } // DeriveKey accepts master secret key masterSecKey and input vector y, and derives a // functional encryption key for the inner product with y. // In case of malformed secret key or input vector that violates the configured // bound, it returns an error. func (s *Paillier) DeriveKey(masterSecKey data.Vector, y data.Vector) (*big.Int, error) { if s.Params.BoundY != nil { if err := y.CheckBound(s.Params.BoundY); err != nil { return nil, err } } return masterSecKey.Dot(y) } // Encrypt encrypts input vector x with the provided master public key. // It returns a ciphertext vector. If encryption failed, error is returned. func (s *Paillier) Encrypt(x, masterPubKey data.Vector) (data.Vector, error) { if s.Params.BoundX != nil { if err := x.CheckBound(s.Params.BoundX); err != nil { return nil, err } } // generate a randomness for the encryption nOver4 := new(big.Int).Quo(s.Params.N, big.NewInt(4)) r, err := rand.Int(rand.Reader, nOver4) if err != nil { return nil, err } // encrypt x under randomness r cipher := make(data.Vector, s.Params.L+1) // c_0 = g^r in Z_n^2 c0 := new(big.Int).Exp(s.Params.G, r, s.Params.NSquare) cipher[0] = c0 for i := 0; i < s.Params.L; i++ { // c_i = (1 + x_i * n) * pubKey_i^r in Z_n^2 t1 := new(big.Int).Mul(x[i], s.Params.N) t1.Add(t1, big.NewInt(1)) t2 := new(big.Int).Exp(masterPubKey[i], r, s.Params.NSquare) ct := new(big.Int).Mul(t1, t2) ct.Mod(ct, s.Params.NSquare) cipher[i+1] = ct } return cipher, nil } // Decrypt accepts the encrypted vector, functional encryption key, and // a vector y. It returns the inner product of x and y. func (s *Paillier) Decrypt(cipher data.Vector, key *big.Int, y data.Vector) (*big.Int, error) { if s.Params.BoundX != nil { if err := y.CheckBound(s.Params.BoundY); err != nil { return nil, err } } // tmp value cX is calculated as (prod_{i=1 to l} c_i^y_i) * c_0^(-key) in Z_n^2 keyNeg := new(big.Int).Neg(key) cX := internal.ModExp(cipher[0], keyNeg, s.Params.NSquare) for i, ct := range cipher[1:] { t1 := internal.ModExp(ct, y[i], s.Params.NSquare) cX.Mul(cX, t1) cX.Mod(cX, s.Params.NSquare) } // decryption is calculated as (cX-1 mod n^2)/n cX.Sub(cX, big.NewInt(1)) cX.Mod(cX, s.Params.NSquare) ret := new(big.Int).Quo(cX, s.Params.N) // if the return value is negative this is seen as the above ret being // greater than n/2; in this case ret = ret - n nHalf := new(big.Int).Quo(s.Params.N, big.NewInt(2)) if ret.Cmp(nHalf) == 1 { ret.Sub(ret, s.Params.N) } return ret, nil } ================================================ FILE: innerprod/fullysec/paillier_multi.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package fullysec import ( "fmt" "math/big" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/internal" "github.com/fentec-project/gofe/sample" ) // PaillierMulti represents a multi input variant of the // underlying Paillier scheme based on // Abdalla, Catalano, Fiore, Gay, and Ursu: // "Multi-Input Functional Encryption for Inner Products: // Function-Hiding Realizations and Constructions without Pairings". // The participants in the scheme are clients and a central authority. // The central authority generates keys for each client so that client i // encrypts vector x_i. The scheme allows the central authority to // generate a key_Y, depending on a matrix Y with rows y_i, so that // given key_y and the ciphertext the decryptor can compute value // Σ_i (sum of dot products). // PaillierMulti is a struct in PaillierMulti scheme, that holds // all the shared parameters, and can represent the central authority // or the decryptor. type PaillierMulti struct { NumClients int BoundX *big.Int BoundY *big.Int *Paillier } // PaillerMultiClient represents a single client for the PaillierMulti scheme. type PaillierMultiClient struct { BoundX *big.Int BoundY *big.Int *Paillier } // NewPaillierMulti configures a new instance of the scheme. // It accepts the number of clients, the length of // input vectors l, security parameter lambda (for number of // bits of security the bit length of primes p and q to be generated // (the scheme is operating in the Z_{(pq)^2} group), and a bound by // which coordinates of input vectors are bounded. It generates all // the remaining parameters to be shared. // If you are not sure how to choose lambda and bitLen, setting // lambda = 128, bitLen = 1024 will result in a scheme that is believed // to have 128 bit security. // // It returns an error in case the underlying Paillier scheme // instances could not be properly instantiated. func NewPaillierMulti(numClients, l, lambda, bitLength int, boundX, boundY *big.Int) (*PaillierMulti, error) { var newBoundX *big.Int newBoundX = nil if boundX != nil && boundY != nil { newBoundX = new(big.Int).Mul(boundX, big.NewInt(3)) } paillier, err := NewPaillier(l, lambda, bitLength, newBoundX, boundY) if err != nil { return nil, err } // the bound of the underlying Paillier scheme is set to // nil value since the scheme will be used to encrypt // values summed with one time pad, thus arbitrary big paillier.Params.BoundX = nil paillier.Params.BoundY = nil return &PaillierMulti{ NumClients: numClients, BoundY: boundY, BoundX: boundX, Paillier: paillier, }, nil } // NewPaillierMultiClientFromParams takes the bounds and configuration parameters of an underlying // Paillier scheme instance, and instantiates a new PaillierMultiClient. // // It returns a new PaillierMultiClient instance. func NewPaillierMultiClientFromParams(params *PaillierParams, boundX, boundY *big.Int) *PaillierMultiClient { return &PaillierMultiClient{ BoundY: boundY, BoundX: boundX, Paillier: &Paillier{params}, } } // NewPaillierMultiFromParams takes the number of clients, bound and configuration // parameters of an existing Paillier scheme instance, and reconstructs // the scheme with the same configuration parameters. // // It returns a new PaillierMulti instance. func NewPaillierMultiFromParams(numClients int, boundX, boundY *big.Int, params *PaillierParams) *PaillierMulti { return &PaillierMulti{ NumClients: numClients, BoundX: boundX, BoundY: boundY, Paillier: &Paillier{params}, } } // PaillierMultiSecKeys is a struct containing keys and one time pads for all the clients in // the Paillier multi input scheme. type PaillierMultiSecKeys struct { Msk data.Matrix Mpk data.Matrix Otp data.Matrix } // GenerateMasterKeys generates keys and one time pads for all the clients. // It returns an error in case values could not be generated. func (dm *PaillierMulti) GenerateMasterKeys() (*PaillierMultiSecKeys, error) { multiMsk := make([]data.Vector, dm.NumClients) multiMpk := make([]data.Vector, dm.NumClients) multiOtp := make([]data.Vector, dm.NumClients) for i := 0; i < dm.NumClients; i++ { msk, mpk, err := dm.Paillier.GenerateMasterKeys() if err != nil { return nil, fmt.Errorf("error in master key generation") } multiMsk[i] = msk multiMpk[i] = mpk otp, err := data.NewRandomVector(dm.Params.L, sample.NewUniform(dm.Params.NSquare)) if err != nil { return nil, fmt.Errorf("error in random vector generation") } multiOtp[i] = otp } secKeys := &PaillierMultiSecKeys{ Msk: data.Matrix(multiMsk), Mpk: data.Matrix(multiMpk), Otp: data.Matrix(multiOtp), } return secKeys, nil } // Encrypt generates a ciphertext from the input vector x // with the provided public key of the underlying Paillier scheme and // one-time pad otp (which are a part of the secret key). It returns // the ciphertext vector. If the encryption failed, error is returned. func (e *PaillierMultiClient) Encrypt(x data.Vector, pubKey, otp data.Vector) (data.Vector, error) { if e.BoundX != nil { if err := x.CheckBound(e.BoundX); err != nil { return nil, err } } xAddOtp := x.Add(otp) xAddOtp = xAddOtp.Mod(e.Params.NSquare) return e.Paillier.Encrypt(xAddOtp, pubKey) } // PaillierMultiDerivedKey is a functional encryption key for PaillierMulti scheme. type PaillierMultiDerivedKey struct { Keys []*big.Int Z *big.Int // Σ where u_i is OTP key for i-th client } // DeriveKey takes master secret key and a matrix y comprised // of input vectors, and returns the functional encryption key. // In case the key could not be derived, it returns an error. func (dm *PaillierMulti) DeriveKey(secKey *PaillierMultiSecKeys, y data.Matrix) (*PaillierMultiDerivedKey, error) { if dm.BoundY != nil { if err := y.CheckBound(dm.BoundY); err != nil { return nil, err } } z, err := secKey.Otp.Dot(y) if err != nil { return nil, err } z.Mod(z, dm.Params.NSquare) derivedKeys := make([]*big.Int, dm.NumClients) for i := 0; i < dm.NumClients; i++ { derivedKey, err := dm.Paillier.DeriveKey(secKey.Msk[i], y[i]) if err != nil { return nil, err } derivedKeys[i] = derivedKey } return &PaillierMultiDerivedKey{derivedKeys, z}, nil } // Decrypt accepts an array of ciphers, i.e. an array of encrypted vectors, // functional encryption key, and a matrix y describing the inner-product. // It returns the sum of inner products Σ_i . // If decryption failed, error is returned. func (dm *PaillierMulti) Decrypt(cipher []data.Vector, key *PaillierMultiDerivedKey, y data.Matrix) (*big.Int, error) { if dm.BoundY != nil { if err := y.CheckBound(dm.BoundY); err != nil { return nil, err } } r := big.NewInt(0) for k := 0; k < dm.NumClients; k++ { keyNeg := new(big.Int).Neg(key.Keys[k]) cX := internal.ModExp(cipher[k][0], keyNeg, dm.Params.NSquare) for i, ct := range cipher[k][1:] { t1 := internal.ModExp(ct, y[k][i], dm.Params.NSquare) cX.Mul(cX, t1) cX.Mod(cX, dm.Params.NSquare) } r.Add(r, cX) r.Mod(r, dm.Params.NSquare) } z := new(big.Int).Mul(dm.Params.N, key.Z) z.Sub(big.NewInt(1), z) z.Mod(z, dm.Params.NSquare) r.Add(r, z) r.Mod(r, dm.Params.NSquare) // decryption is calculated as (cX-1 mod n^2)/n r.Sub(r, big.NewInt(1)) r.Mod(r, dm.Params.NSquare) ret := new(big.Int).Quo(r, dm.Params.N) // if the return value is negative this is seen as the above ret being // greater than n/2; in this case ret = ret - n nHalf := new(big.Int).Quo(dm.Params.N, big.NewInt(2)) if ret.Cmp(nHalf) == 1 { ret.Sub(ret, dm.Params.N) } return ret, nil } ================================================ FILE: innerprod/fullysec/paillier_multi_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package fullysec_test import ( "math/big" "testing" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/innerprod/fullysec" "github.com/fentec-project/gofe/sample" "github.com/stretchr/testify/assert" ) func TestFullySec_PaillierMulti(t *testing.T) { // choose meta-parameters for the scheme numClients := 6 l := 5 bound := big.NewInt(1024) sampler := sample.NewUniformRange(new(big.Int).Add(new(big.Int).Neg(bound), big.NewInt(1)), bound) // build the central authority for the scheme with parameters that should provide 128-bit security var paillierMulti *fullysec.PaillierMulti var err error paillierMulti, err = fullysec.NewPaillierMulti(numClients, l, 128, 1024, bound, bound) if err != nil { t.Fatalf("Failed to initialize multi input inner product: %v", err) } // we simulate different clients which might be on different machines (this means "multi-input"), clients := make([]*fullysec.PaillierMultiClient, numClients) for i := 0; i < numClients; i++ { clients[i] = fullysec.NewPaillierMultiClientFromParams(paillierMulti.Params, bound, bound) } // the central authority generates keys for all the clients secKeys, err := paillierMulti.GenerateMasterKeys() if err != nil { t.Fatalf("Error during keys generation: %v", err) } // pick a matrix that represent the collection of inner-product vectors y_i y, err := data.NewRandomMatrix(numClients, l, sampler) if err != nil { t.Fatalf("Error during matrix generation: %v", err) } // each client encrypts its vector x_i collectedX := make([]data.Vector, numClients) // solely for checking whether Encrypt/Decrypt works properly ciphertexts := make([]data.Vector, numClients) for i := 0; i < numClients; i++ { x, err := data.NewRandomVector(l, sampler) // x_i possessed and chosen by client[i] if err != nil { t.Fatalf("Error during random vector generation: %v", err) } collectedX[i] = x c, err := clients[i].Encrypt(x, secKeys.Mpk[i], secKeys.Otp[i]) if err != nil { t.Fatalf("Error during encryption: %v", err) } ciphertexts[i] = c } // central authority derives the key for the decryptor derivedKey, err := paillierMulti.DeriveKey(secKeys, y) if err != nil { t.Fatalf("Error during key derivation: %v", err) } // we simulate the decryptor decryptor := fullysec.NewPaillierMultiFromParams(numClients, bound, bound, paillierMulti.Params) // decryptor decrypts the value xy, err := decryptor.Decrypt(ciphertexts, derivedKey, y) if err != nil { t.Fatalf("Error during decryption: %v", err) } // we check if the decrypted value is correct xMatrix, err := data.NewMatrix(collectedX) if err != nil { t.Fatalf("Error during collection of vectors to be encrypted: %v", err) } xyCheck, err := xMatrix.Dot(y) if err != nil { t.Fatalf("Error during inner product calculation: %v", err) } assert.Equal(t, xy.Cmp(xyCheck), 0, "obtained incorrect inner product") } ================================================ FILE: innerprod/fullysec/paillier_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package fullysec_test import ( "math/big" "testing" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/innerprod/fullysec" "github.com/fentec-project/gofe/sample" "github.com/stretchr/testify/assert" ) func TestFullySec_Paillier(t *testing.T) { l := 50 boundX := new(big.Int).Exp(big.NewInt(2), big.NewInt(64), nil) boundY := new(big.Int).Exp(big.NewInt(2), big.NewInt(64), nil) samplerX := sample.NewUniformRange(new(big.Int).Neg(boundX), boundX) samplerY := sample.NewUniformRange(new(big.Int).Neg(boundY), boundY) bitLength := 512 lambda := 128 paillier, err := fullysec.NewPaillier(l, lambda, bitLength, boundX, boundY) if err != nil { t.Fatalf("Error during simple inner product creation: %v", err) } masterSecKey, masterPubKey, err := paillier.GenerateMasterKeys() if err != nil { t.Fatalf("Error during master key generation: %v", err) } y, err := data.NewRandomVector(l, samplerY) if err != nil { t.Fatalf("Error during random generation: %v", err) } key, err := paillier.DeriveKey(masterSecKey, y) if err != nil { t.Fatalf("Error during key derivation: %v", err) } x, err := data.NewRandomVector(l, samplerX) if err != nil { t.Fatalf("Error during random generation: %v", err) } // simulate the instantiation of encryptor (which should be given masterPubKey) encryptor := fullysec.NewPaillierFromParams(paillier.Params) ciphertext, err := encryptor.Encrypt(x, masterPubKey) if err != nil { t.Fatalf("Error during encryption: %v", err) } xy, err := paillier.Decrypt(ciphertext, key, y) if err != nil { t.Fatalf("Error during decryption") } xyCheck, err := x.Dot(y) if err != nil { t.Fatalf("Error during inner product calculation") } assert.Equal(t, xy.Cmp(xyCheck), 0, "Original and decrypted values should match") } ================================================ FILE: innerprod/fullysec/part_fh_ipe.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package fullysec import ( "fmt" "math/big" "github.com/fentec-project/bn256" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/internal/dlog" "github.com/fentec-project/gofe/sample" ) // PartFHIPEParams includes public parameters for the partially // function hiding inner product scheme. // L (int): The length of vectors to be encrypted. // Bound (*big.Int): The value by which coordinates of vectors x and y are bounded. type PartFHIPEParams struct { L int Bound *big.Int } // PartFHIPE represents a partially function hiding inner product // FE scheme. A partially function hiding scheme is a public // key FE scheme that allows to encrypt vectors and produce FE // keys to be able to decrypt only the inner product of the encryption // and chosen vector without revealing the encrypted or FE key vector. // Public key encryption allows to encrypt only vectors from a chosen // subspace. This way a functional encryption key does not reveal // its corresponding inner product vector. Decryption of a // ciphertext using FE key can be done without knowing the function. // Additionally, owner of the secret key can encrypt any vector. // // The scheme is based on the paper by Romain Gay: // "A New Paradigm for Public-Key Functional Encryption for // Degree-2 Polynomials". type PartFHIPE struct { Params *PartFHIPEParams } // NewPartFHIPE configures a new instance of the scheme. // It accepts the length of input vectors l, and a bound by which // the absolute values of the coordinates of input vectors are bounded. // // It returns an error in case the scheme could not be properly // configured, or if precondition 2* l * bound² is >= order of the cyclic // group. func NewPartFHIPE(l int, bound *big.Int) (*PartFHIPE, error) { var b *big.Int if bound != nil { b = new(big.Int).Set(bound) bSquared := new(big.Int).Mul(bound, bound) upper := new(big.Int).Mul(big.NewInt(int64(2*l)), bSquared) if upper.Cmp(bn256.Order) > 0 { return nil, fmt.Errorf("bound and l too big for the group") } } return &PartFHIPE{ Params: &PartFHIPEParams{ L: l, Bound: b, }, }, nil } // NewPartFHIPEFromParams takes configuration parameters of an existing // PartFHIPE instance, and reconstructs the scheme with the same configuration // parameters. It returns a new PartFHIPE instance. func NewPartFHIPEFromParams(params *PartFHIPEParams) *PartFHIPE { return &PartFHIPE{ Params: params, } } // PartFHIPESecKey is a secret key for the partially function hiding // inner product scheme. type PartFHIPESecKey struct { B data.Vector V data.Matrix U data.Matrix } // PartFHIPEPubKey is a public key for the partially function hiding // inner product scheme. type PartFHIPEPubKey struct { A data.VectorG1 Ua data.VectorG1 VtM data.MatrixG1 M data.Matrix MG1 data.MatrixG1 } // GenerateKeys generates a master secret key and public key for the scheme. // A matrix M needs to be specified so that the generated public key will // allow to encrypt arbitrary vector in the span on the columns of M. // It returns an error in case the keys could not be generated. func (d *PartFHIPE) GenerateKeys(M data.Matrix) (*PartFHIPEPubKey, *PartFHIPESecKey, error) { if d.Params.L != M.Rows() { return nil, nil, fmt.Errorf("dimensions of the given matrix do not match dimensions of the scheme") } sampler := sample.NewUniform(bn256.Order) aVec := make(data.Vector, 2) aVec[0] = big.NewInt(1) x, err := sampler.Sample() if err != nil { return nil, nil, err } aVec[1] = x b := make(data.Vector, 2) b[0] = big.NewInt(1) x, err = sampler.Sample() if err != nil { return nil, nil, err } b[1] = x a := aVec.MulG1() U, err := data.NewRandomMatrix(d.Params.L+2, 2, sampler) if err != nil { return nil, nil, err } V, err := data.NewRandomMatrix(d.Params.L, 2, sampler) if err != nil { return nil, nil, err } UaVec, err := U.MulVec(aVec) if err != nil { return nil, nil, err } UaVec = UaVec.Mod(bn256.Order) Ua := UaVec.MulG1() VtMMat, err := V.Transpose().Mul(M) if err != nil { return nil, nil, err } VtMMat = VtMMat.Mod(bn256.Order) VtM := VtMMat.MulG1() MG1 := M.MulG1() return &PartFHIPEPubKey{A: a, Ua: Ua, VtM: VtM, M: M.Copy(), MG1: MG1}, &PartFHIPESecKey{B: b, V: V, U: U}, nil } // DeriveKey takes input vector y and master secret key, and returns the // functional encryption key. In case the key could not be derived, it // returns an error. func (d *PartFHIPE) DeriveKey(y data.Vector, secKey *PartFHIPESecKey) (data.VectorG2, error) { if len(y) != d.Params.L { return nil, fmt.Errorf("the dimension of the given vector does not match the dimension of the scheme") } if d.Params.Bound != nil { if err := y.CheckBound(d.Params.Bound); err != nil { return nil, err } } sampler := sample.NewUniform(bn256.Order) s, err := sampler.Sample() if err != nil { return nil, err } bs := secKey.B.MulScalar(s) bs = bs.Mod(bn256.Order) Vbs, err := secKey.V.MulVec(bs) if err != nil { return nil, err } YVbs := y.Add(Vbs) YVbs = YVbs.Mod(bn256.Order) key2 := append(bs, YVbs...) key1, err := secKey.U.Transpose().MulVec(key2) if err != nil { return nil, err } key1 = key1.Neg() key1 = key1.Mod(bn256.Order) key := append(key1, key2...) return key.MulG2(), nil } // Encrypt on input vector t encrypts vector x = Mt with the provided public key // (matrix M is specified in the public key). It returns a ciphertext vector. // Entries of Mt should not be greater then bound. // If encryption fails, an error is returned. func (d *PartFHIPE) Encrypt(t data.Vector, pubKey *PartFHIPEPubKey) (data.VectorG1, error) { x, err := pubKey.M.MulVec(t) if err != nil { return nil, err } if d.Params.Bound != nil { if err := x.CheckBound(d.Params.Bound); err != nil { return nil, err } } sampler := sample.NewUniform(bn256.Order) r, err := sampler.Sample() if err != nil { return nil, err } c := pubKey.A.MulScalar(r) Uc := pubKey.Ua.MulScalar(r) Mt := pubKey.MG1.MulVector(t) VtMt := pubKey.VtM.MulVector(t) VtMxNeg := VtMt.Neg() cipher2 := append(VtMxNeg, Mt...) cipher2add := cipher2.Add(Uc) cipher := append(c, cipher2add...) return cipher, nil } // SecEncrypt encrypts an arbitrary vector x using master secret key and // public key. It returns a ciphertext vector. If encryption failed, // an error is returned. func (d *PartFHIPE) SecEncrypt(x data.Vector, pubKey *PartFHIPEPubKey, secKey *PartFHIPESecKey) (data.VectorG1, error) { if len(x) != d.Params.L { return nil, fmt.Errorf("the dimension of the given vector does not match the dimension of the scheme") } if d.Params.Bound != nil { if err := x.CheckBound(d.Params.Bound); err != nil { return nil, err } } sampler := sample.NewUniform(bn256.Order) r, err := sampler.Sample() if err != nil { return nil, err } c := pubKey.A.MulScalar(r) Uc := pubKey.Ua.MulScalar(r) Vtx, err := secKey.V.Transpose().MulVec(x) if err != nil { return nil, err } Vtx = Vtx.Neg().Mod(bn256.Order) VtxG1 := Vtx.MulG1() xG1 := x.MulG1() cipher2 := append(VtxG1, xG1...) cipher2add := cipher2.Add(Uc) cipher := append(c, cipher2add...) return cipher, nil } // PartDecrypt accepts the encrypted vector and functional encryption key. It // returns the value d*[bn256.GT] where d is the inner product of x and y. // To obtain a final result, calculating the discrete logarithm is needed. func (d *PartFHIPE) PartDecrypt(cipher data.VectorG1, feKey data.VectorG2) (*bn256.GT, error) { if len(cipher) != d.Params.L+4 || len(feKey) != d.Params.L+4 { return nil, fmt.Errorf("the length of FE key or ciphertext does not match the dimension of the scheme") } dec := new(bn256.GT).ScalarBaseMult(big.NewInt(0)) for i := 0; i < d.Params.L+4; i++ { pairedI := bn256.Pair(cipher[i], feKey[i]) dec = new(bn256.GT).Add(pairedI, dec) } return dec, nil } // Decrypt accepts the encrypted vector and functional encryption key. // It returns the inner product of x and y. If decryption failed, error is returned. func (d *PartFHIPE) Decrypt(cipher data.VectorG1, feKey data.VectorG2) (*big.Int, error) { dec, err := d.PartDecrypt(cipher, feKey) if err != nil { return nil, err } calc := dlog.NewCalc().InBN256().WithNeg() if d.Params.Bound != nil { bSquared := new(big.Int).Mul(d.Params.Bound, d.Params.Bound) bound := new(big.Int).Mul(big.NewInt(int64(d.Params.L)), bSquared) calc = calc.WithBound(bound) } res, err := calc.BabyStepGiantStep(dec, new(bn256.GT).ScalarBaseMult(big.NewInt(1))) return res, err } ================================================ FILE: innerprod/fullysec/part_fh_ipe_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package fullysec_test import ( "math/big" "testing" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/innerprod/fullysec" "github.com/fentec-project/gofe/sample" "github.com/stretchr/testify/assert" ) func TestPartFHIPE(te *testing.T) { // choose parameters for the encryption and build the scheme l := 50 bound := big.NewInt(100) boundNeg := new(big.Int).Add(new(big.Int).Neg(bound), big.NewInt(1)) sampler := sample.NewUniformRange(boundNeg, bound) partfhipe, err := fullysec.NewPartFHIPE(l, bound) if err != nil { te.Fatalf("Error during scheme creation: %v", err) } // choose a subspace in which encryption will be allowed k := 5 // dimension of the subspace // the subspace is given by the columns of the matrix m // we sample m with not too big inputs samplerM := sample.NewUniform(new(big.Int).Div(bound, big.NewInt(int64(k)))) m, err := data.NewRandomMatrix(l, k, samplerM) if err != nil { te.Fatalf("Error during random matrix generation: %v", err) } // generate public and secret key based on matrix m pubKey, secKey, err := partfhipe.GenerateKeys(m) if err != nil { te.Fatalf("Error during master key generation: %v", err) } // simulate the instantiation of an encryptor encryptor := fullysec.NewPartFHIPEFromParams(partfhipe.Params) // sample a vector x that the encryptor will encrypt with public key; // the vector is described with k dimensional vector t such that // x = Mt sampler2 := sample.NewUniformRange(big.NewInt(-1), big.NewInt(2)) t, err := data.NewRandomVector(k, sampler2) if err != nil { te.Fatalf("Error during random vector generation: %v", err) } // encrypt the vector ciphertextMt, err := encryptor.Encrypt(t, pubKey) if err != nil { te.Fatalf("Error during encryption: %v", err) } // the owner of the secret key encrypts an arbitrary vector x, err := data.NewRandomVector(l, sampler) if err != nil { te.Fatalf("Error during random vector generation: %v", err) } ciphertextX, err := partfhipe.SecEncrypt(x, pubKey, secKey) if err != nil { te.Fatalf("Error during encryption with secret key: %v", err) } // sample an inner product vector y, err := data.NewRandomVector(l, sampler) if err != nil { te.Fatalf("Error during random vecotr generation: %v", err) } // derive a functional key for vector y feKey, err := partfhipe.DeriveKey(y, secKey) if err != nil { te.Fatalf("Error during key derivation: %v", err) } // simulate a decryptor decryptor := fullysec.NewPartFHIPEFromParams(partfhipe.Params) // decryptor decrypts the inner-product y^TMt without knowing // vectors Mt and y yMt, err := decryptor.Decrypt(ciphertextMt, feKey) if err != nil { te.Fatalf("Error during decryption: %v", err) } // and decrypts the inner-product x^Ty without knowing // vectors x and y yx, err := decryptor.Decrypt(ciphertextX, feKey) if err != nil { te.Fatalf("Error during decryption: %v", err) } // check the correctness of the results Mt, err := m.MulVec(t) if err != nil { te.Fatalf("Error calculating the inner product: %v", err) } yMtCheck, err := y.Dot(Mt) if err != nil { te.Fatalf("Error calculating the inner product: %v", err) } yxCheck, err := y.Dot(x) if err != nil { te.Fatalf("Error calculating the inner product: %v", err) } assert.Equal(te, yMt.Cmp(yMtCheck), 0, "obtained incorrect inner product") assert.Equal(te, yx.Cmp(yxCheck), 0, "obtained incorrect inner product") } ================================================ FILE: innerprod/simple/ddh.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package simple import ( "fmt" "math/big" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/internal" "github.com/fentec-project/gofe/internal/dlog" "github.com/fentec-project/gofe/internal/keygen" "github.com/fentec-project/gofe/sample" ) // DDHParams represents configuration parameters for the DDH scheme instance. type DDHParams struct { // length of input vectors x and y L int // The value by which coordinates of input vectors x and y are bounded. Bound *big.Int // Generator of a cyclic group Z_P: G^(Q) = 1 (mod P). G *big.Int // Modulus - we are operating in a cyclic group Z_P. P *big.Int // Order of the generator G. Q *big.Int } // DDH represents a scheme instantiated from the DDH assumption, // based on the DDH variant by // Abdalla, Bourse, De Caro, and Pointchev: // "Simple Functional Encryption Schemes for Inner Products". type DDH struct { Params *DDHParams } // NewDDH configures a new instance of the scheme. // It accepts the length of input vectors l, the bit length of the // modulus (we are operating in the Z_p group), and a bound by which // coordinates of input vectors are bounded. // // It returns an error in case the scheme could not be properly // configured, or if precondition l * bound² is >= order of the cyclic // group. func NewDDH(l, modulusLength int, bound *big.Int) (*DDH, error) { key, err := keygen.NewElGamal(modulusLength) if err != nil { return nil, err } if new(big.Int).Mul(big.NewInt(int64(2*l)), new(big.Int).Exp(bound, big.NewInt(2), big.NewInt(0))).Cmp(key.Q) > 0 { return nil, fmt.Errorf("2 * l * bound^2 should be smaller than group order") } sip := DDH{ Params: &DDHParams{ L: l, Bound: bound, G: key.G, P: key.P, Q: key.Q, }, } return &sip, nil } // NewDDHPrecomp configures a new instance of the scheme based on // precomputed prime numbers and generators. // It accepts the length of input vectors l, the bit length of the // modulus (we are operating in the Z_p group), and a bound by which // coordinates of input vectors are bounded. The modulus length should // be one of values 1024, 1536, 2048, 2560, 3072, or 4096. // // It returns an error in case the scheme could not be properly // configured, or if precondition l * bound² is >= order of the cyclic // group. func NewDDHPrecomp(l, modulusLength int, bound *big.Int) (*DDH, error) { zero := big.NewInt(0) one := big.NewInt(1) two := big.NewInt(2) g := new(big.Int) p := new(big.Int) if modulusLength == 1024 { g.SetString("34902160241479276675539633849372382885917193816560610471607073855548755350834003692485735908635894317735639518678334280193650806072183057417077181724192674928134805218882803812978345229222559213790765817899845072682155064387311523738581388872686127675360979304234957611566801734164757915959042140104663977828", 10) p.SetString("166211269243229118758738154756726384542659478479960313411107431885216572625212662756677338184675400324411541201832214281445670912135683272416408753424543622705770319923251281963485084208425069817917631106045349238686234860629044433560424091289406000897029571960128048529362925472176997104870527051276406995203", 10) } else if modulusLength == 1536 { g.SetString("676416913692519694440150163403654362412279108516867264953779609011365998625435399420336578530015558254310139891236630566729665914687641028600402606957815727025192669238117788115237116562468680376464346714542467465836552396661693422160454402926392749202926871877212792118140354124110927269910674002861908621272286950597240072605316784317536178700101838123530590145680002962405974024190384775185108002307650499125333676880320808656556635493186351335151559453463208", 10) p.SetString("1851297899986638926486011430658634631676522135433726749065856232802142091866650774719879427474637700607873256035038534449089405369134066444876856913629831069906506096279113968447116822488133963417347136141052507685108634240736100862550194947326287783557220764070479431781692630708747550712729778398000353165406458520850089303530985563143326919073190605085889925484113854496074216626577246143598303709289292397203458923541841135799203967503522114881404128535647507", 10) } else if modulusLength == 2048 { g.SetString("4006960929413042209594165215465319088439374252008797022450541422457034721098828647078778605657155669917104962611933792130890703423519992986737966991597160684973795472419962788730248050852176194215699504914899438223683843401963466624139534923052671383315398134823370041633710463630745156269175253639670460050105594663691338308037509280576148624454011047879615100156717631945194107791315234171086603775159708325087759679758438868772220133433497821899045165244202228696902434100209752952701657306825368599999359102329396520012735146260911352901326915877502873633420811221206110021993351144711002138373506576799781061829", 10) p.SetString("28884237504713658990682089080899862128005980675308910325841161962760155725037929764087367167449843609136681034352509183117742758446654629096509285354423361556493020266963222508540306384896802796001914743293196010488452478370041404523014215612960481024232879327123268440037633547483165934132901270561772860319969916305482525766132307669097012989986613879246932730824899649301621408341438037745468033187743673001187803377254713546325789438300798311106106322698517805307792059495696632070953526611920926003483451787562399452650878943515646786958216714025307572678422373120397225912926110031401983688860264234966561627699", 10) } else if modulusLength == 2560 { g.SetString("283408881721750179985507845260248881237898607313021593637913985490973593382472548378053368228310040484438920416918021737085067966444840449068073874437662089659563355479608930182263107110969154912883370207053052795964658868443319273167870311282045348396320159912742394374241864712808383029954025256232806465551969466207671603658677963161975454703127476120201164519187150268352527923664649275471494757270139533433456630363925187498055211365480086561354743681517539297815712218419607006668655891574362066382949706266666189227897710299445185100212256741698216505337617571970963008519334554537811591236478130526432239803909461119767954934793813410765013072006162612226471775059215628326278458577643374735250370115470812597459244082296191871275203831471332697557979904062571849", 10) p.SetString("403126381462544353337185732949672984701995200694926494258456907009253008321275627278199160008680481542251933923210212978304159426174307419863781623411302777276318286800690060166638633211627521530173324015527027650548199185539205697958056639406116068885574865579676651743820636201007864067569576455725489531113260031526605827601510665037511961715114944815619491261828558745083975042855063688267346905844510423020844412350570902289599734320004108557140241966071165594059732527795488131297017205383953304055105007982366596746708951250486384299368612656872813778220074826250625689603663742175288397398948456522281031888042417278385238985218731264092285591879578299600853004336936458454638992426900228708418575870946630137618851131144232868141478901063119847104013555395370887", 10) } else if modulusLength == 3072 { g.SetString("3696261717511684685041982088526264061294500298114921057816954458306445697150348237912729036967670872345042594238223317055749478029025374644864924550052402546275985983344583674703146236623453822520422465163020824494790581472736649085281450730460445260696087738043872307629635997875332076478424042345012004769107421873566499123042621978973433575500345010912635742477932006291250637245855027695943163956584173316781442078828050076620331751405548730676363847526959436516279320074682721438642683731766682502490935962962293815202487144775533102010333956118641968798500514719248831145108532912211817219793191951880318961073149276914867129023978524587935704313755469570162971499124682746476415187933097132047611840762510892175328320025164466873845777990557296853549970943298347924080102740724512079409979152285019931666423541870247789529268168448010024121369388707140296446100906359619586133848407970098685310317291828335700424602208", 10) p.SetString("4387756306134544957818467663802660683665166110605728231080818705443663402154316615145921798856363268744945754470238000282108344905251127487705736550297997444150840902348669718478564904142834154197029830975532074167513046443903186309497214496864577129616824062991068960005865144004932069025136224356325248036029606434443391988386519658751798077031844645051726026696307027395796695909035405241040411794836124123435225690961994089776517262574417789067836840997650095451062948856617211542724543995145259735683916440579956961657374517806591607068842498749297993409884001044324428640569001916341503645559748760311343179943896427393009949062735145363544745972252566600994034655540841225414736222780096833045470605544717177880459300618917961703559234544541206877026518430276932498602360341258899345739335298856394124351357206871568254540730107127298623178526868418799471896060015463201459762913197633841160710893895836663035998106119", 10) } else if modulusLength == 4096 { g.SetString("51665588681810560577916524923861643358980285220048008212528567741884121491554604183472728540139463099618903178110360757930742372390027135064809646425064896539133721148335557788263239281487173350543811713890328584918216783142094297306639941000480756707312457878765754357205186485080839623690156744636468433787780205323460166423447602447200754978133176713947189000663528355089645281397174452923418212485422962705227706103188302892660448134233848971142570881089940852441776074246332915421265800026335300100610273942459340241610730244726628211914068945587128124478812632725838440727321816905181830592204023095726270782834020990986443265625389712733369116937470448592846480352222814297792606318850361699893703272484112273500581408730519942517586496563772194165844831300501908379990979449691597045730512107756238377635183257797115883839801779086058652272455400286891699445584526719648220045380141260347316315487340493029966105973850214850475440630205768783542021741101804842248602349004364816943429122368563644935802417389995380389429997320053299323220481603252879925927515844929958940305561718295197935926645561977544440676439150126025681320050786964708227836328341875446457912905977470123640014345655062829575775837287500880054558386787", 10) p.SetString("1022249395832567838406986294560330159176972202126664245047364146720891252715766488477689126342364655087193411078517616569887825896401401223927363505007778278205623713273194552498760148834874746839752870298152746450585455651115247220867383465863156721401567161663838310658875672995951663020449772454232797368263754624173026584111779206080723120076751471597509403139249260220696195263597156452889920392585797464801375940661326779247976331028637271512085826066667631423502199894046717721786935806581428328491087482664043743281068318459302242239861275878019857365021173868449409246193470959347916848019032536247915451026158871684654213802886886213841729258073333569276986893577214659899227179735448593265633219968622571880602115519942763955551007919826002851866939641065270816032435114864853636918330698605282572789904941484540512478406984407320963402583009124880812235841866246441862987563989772424040933513333746472128494254253767426962063553015635240386636751473945937412527996558505231385625318878887383161350102080329744822052478052004574860361461762694379860797225344866320388590336321515376486033237159694567932935601775209663052272120524337888258857351777348841323194553467226791591208931619058871750498804369190487499494069660723", 10) } else { return nil, fmt.Errorf("modulus length should be one of values 1024, 1536, 2048, 2560, 3072, or 4096") } q := new(big.Int).Sub(p, one) q.Div(q, two) if new(big.Int).Mul(big.NewInt(int64(2*l)), new(big.Int).Exp(bound, two, zero)).Cmp(q) > 0 { return nil, fmt.Errorf("2 * l * bound^2 should be smaller than group order") } sip := DDH{ Params: &DDHParams{ L: l, Bound: bound, G: g, P: p, Q: q, }, } return &sip, nil } // NewDDHFromParams takes configuration parameters of an existing // DDH scheme instance, and reconstructs the scheme with same configuration // parameters. It returns a new DDH instance. func NewDDHFromParams(params *DDHParams) *DDH { return &DDH{ Params: params, } } // GenerateMasterKeys generates a pair of master secret key and master // public key for the scheme. It returns an error in case master keys // could not be generated. func (d *DDH) GenerateMasterKeys() (data.Vector, data.Vector, error) { masterSecKey := make(data.Vector, d.Params.L) masterPubKey := make(data.Vector, d.Params.L) sampler := sample.NewUniformRange(big.NewInt(2), d.Params.Q) for i := 0; i < d.Params.L; i++ { x, err := sampler.Sample() if err != nil { return nil, nil, err } y := internal.ModExp(d.Params.G, x, d.Params.P) masterSecKey[i] = x masterPubKey[i] = y } return masterSecKey, masterPubKey, nil } // DeriveKey takes master secret key and input vector y, and returns the // functional encryption key. In case the key could not be derived, it // returns an error. func (d *DDH) DeriveKey(masterSecKey, y data.Vector) (*big.Int, error) { if err := y.CheckBound(d.Params.Bound); err != nil { return nil, err } key, err := masterSecKey.Dot(y) if err != nil { return nil, err } return new(big.Int).Mod(key, d.Params.Q), nil } // Encrypt encrypts input vector x with the provided master public key. // It returns a ciphertext vector. If encryption failed, error is returned. func (d *DDH) Encrypt(x, masterPubKey data.Vector) (data.Vector, error) { if err := x.CheckBound(d.Params.Bound); err != nil { return nil, err } sampler := sample.NewUniformRange(big.NewInt(2), d.Params.Q) r, err := sampler.Sample() if err != nil { return nil, err } ciphertext := make([]*big.Int, len(x)+1) // ct0 = g^r ct0 := new(big.Int).Exp(d.Params.G, r, d.Params.P) ciphertext[0] = ct0 for i := 0; i < len(x); i++ { // ct_i = h_i^r * g^x_i // ct_i = mpk[i]^r * g^x_i t1 := new(big.Int).Exp(masterPubKey[i], r, d.Params.P) t2 := internal.ModExp(d.Params.G, x[i], d.Params.P) ct := new(big.Int).Mod(new(big.Int).Mul(t1, t2), d.Params.P) ciphertext[i+1] = ct } return ciphertext, nil } // Decrypt accepts the encrypted vector, functional encryption key, and // a plaintext vector y. It returns the inner product of x and y. // If decryption failed, error is returned. func (d *DDH) Decrypt(cipher data.Vector, key *big.Int, y data.Vector) (*big.Int, error) { if err := y.CheckBound(d.Params.Bound); err != nil { return nil, err } num := big.NewInt(1) for i, ct := range cipher[1:] { t1 := internal.ModExp(ct, y[i], d.Params.P) num = num.Mod(new(big.Int).Mul(num, t1), d.Params.P) } denom := internal.ModExp(cipher[0], key, d.Params.P) denomInv := new(big.Int).ModInverse(denom, d.Params.P) r := new(big.Int).Mod(new(big.Int).Mul(num, denomInv), d.Params.P) bound := new(big.Int).Mul(big.NewInt(int64(d.Params.L)), new(big.Int).Exp(d.Params.Bound, big.NewInt(2), big.NewInt(0))) calc, err := dlog.NewCalc().InZp(d.Params.P, d.Params.Q) if err != nil { return nil, err } calc = calc.WithNeg() res, err := calc.WithBound(bound).BabyStepGiantStep(r, d.Params.G) return res, err } ================================================ FILE: innerprod/simple/ddh_multi.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package simple import ( "fmt" "math/big" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/sample" ) // DDHMulti represents a multi input variant of the // underlying DDH scheme based on // Abdalla, Catalano, Fiore, Gay, and Ursu: // "Multi-Input Functional Encryption for Inner Products: // Function-Hiding Realizations and Constructions without Pairings". type DDHMulti struct { // number of encryptors Slots int *DDH } // DDHMultiClient represents a multi input variant of the // underlying DDH scheme based on // Abdalla, Catalano, Fiore, Gay, and Ursu: // "Multi-Input Functional Encryption for Inner Products: // Function-Hiding Realizations and Constructions without Pairings". type DDHMultiClient struct { *DDH } // NewDDHMulti configures a new instance of the scheme. // It accepts the number of slots (encryptors), the length of // input vectors l, the bit length of the modulus (we are // operating in the Z_p group), and a bound by which coordinates // of input vectors are bounded. // // It returns an error in case the underlying DDH scheme instances could // not be properly instantiated. func NewDDHMulti(slots, l, modulusLength int, bound *big.Int) (*DDHMulti, error) { ddh, err := NewDDH(l, modulusLength, bound) if err != nil { return nil, err } return &DDHMulti{ Slots: slots, DDH: ddh, }, nil } // NewDDHMultiPrecomp configures a new instance of the scheme based on // precomputed prime numbers and generators.. // It accepts the number of slots (encryptors), the length of // input vectors l, the bit length of the modulus (we are // operating in the Z_p group), and a bound by which coordinates // of input vectors are bounded. The modulus length should // be one of values 1024, 1536, 2048, 2560, 3072, or 4096. // // It returns an error in case the underlying DDH scheme instances could // not be properly instantiated. func NewDDHMultiPrecomp(slots, l, modulusLength int, bound *big.Int) (*DDHMulti, error) { ddh, err := NewDDHPrecomp(l, modulusLength, bound) if err != nil { return nil, err } return &DDHMulti{ Slots: slots, DDH: ddh, }, nil } // NewDDHMultiFromParams takes the number of slots and configuration // parameters of an existing DDH scheme instance, and reconstructs // the scheme with same configuration parameters. // // It returns a new DDHMulti instance. func NewDDHMultiFromParams(slots int, params *DDHParams) *DDHMulti { return &DDHMulti{ Slots: slots, DDH: &DDH{params}, } } // NewDDHMultiClient configures a new instance of the scheme. // It accepts the number of slots (encryptors), the length of // input vectors l, the bit length of the modulus (we are // operating in the Z_p group), and a bound by which coordinates // of input vectors are bounded. // // It returns an error in case the underlying DDH scheme instances could // not be properly instantiated. func NewDDHMultiClient(params *DDHParams) *DDHMultiClient { return &DDHMultiClient{ DDH: &DDH{params}, } } // DDHMultiSecKey is a secret key for DDH multi input scheme. type DDHMultiSecKey struct { Msk data.Matrix OtpKey data.Matrix } // GenerateMasterKeys generates matrices comprised of master secret // keys and master public keys for the scheme. // // It returns an error in case master keys could not be generated. func (dm *DDHMulti) GenerateMasterKeys() (data.Matrix, *DDHMultiSecKey, error) { mskVecs := make([]data.Vector, dm.Slots) mpkVecs := make([]data.Vector, dm.Slots) otpVecs := make([]data.Vector, dm.Slots) for i := 0; i < dm.Slots; i++ { masterSecretKey, masterPublicKey, err := dm.DDH.GenerateMasterKeys() if err != nil { return nil, nil, fmt.Errorf("error in master key generation") } mskVecs[i] = masterSecretKey mpkVecs[i] = masterPublicKey otpVector, err := data.NewRandomVector(dm.Params.L, sample.NewUniform(dm.Params.Bound)) if err != nil { return nil, nil, fmt.Errorf("error in random vector generation") } otpVecs[i] = otpVector } pubKey, err := data.NewMatrix(mpkVecs) if err != nil { return nil, nil, err } secKey, err := data.NewMatrix(mskVecs) if err != nil { return nil, nil, err } otp, err := data.NewMatrix(otpVecs) if err != nil { return nil, nil, err } return pubKey, &DDHMultiSecKey{secKey, otp}, nil } // Encrypt generates a ciphertext from the input vector x // with the provided public key and one-time pad otp (which // is a part of the secret key). It returns the ciphertext vector. // If encryption failed, error is returned. func (e *DDHMultiClient) Encrypt(x data.Vector, pubKey, otp data.Vector) (data.Vector, error) { if err := x.CheckBound(e.Params.Bound); err != nil { return nil, err } otp = x.Add(otp) otpModulo := otp.Mod(e.Params.Bound) return e.DDH.Encrypt(otpModulo, pubKey) } // DDHMultiDerivedKey is functional encryption key for DDH Scheme. type DDHMultiDerivedKey struct { Keys data.Vector OTPKey *big.Int } // DeriveKey takes master secret key and a matrix y comprised // of input vectors, and returns the functional encryption key. // In case the key could not be derived, it returns an error. func (dm *DDHMulti) DeriveKey(secKey *DDHMultiSecKey, y data.Matrix) (*DDHMultiDerivedKey, error) { if err := y.CheckBound(dm.Params.Bound); err != nil { return nil, err } z, err := secKey.OtpKey.Dot(y) if err != nil { return nil, err } z.Mod(z, dm.Params.Bound) derivedKeys := make([]*big.Int, dm.Slots) for i := 0; i < dm.Slots; i++ { derivedKey, err := dm.DDH.DeriveKey(secKey.Msk[i], y[i]) if err != nil { return nil, err } derivedKeys[i] = derivedKey } return &DDHMultiDerivedKey{data.NewVector(derivedKeys), z}, nil } // Decrypt accepts the matrix cipher comprised of encrypted vectors, // functional encryption key, and a matrix y comprised of plaintext vectors. // It returns the sum of inner products. // If decryption failed, error is returned. func (dm *DDHMulti) Decrypt(cipher []data.Vector, key *DDHMultiDerivedKey, y data.Matrix) (*big.Int, error) { if err := y.CheckBound(dm.Params.Bound); err != nil { return nil, err } sum := big.NewInt(0) for i := 0; i < dm.Slots; i++ { c, err := dm.DDH.Decrypt(cipher[i], key.Keys[i], y[i]) if err != nil { return nil, err } sum.Add(sum, c) } res := new(big.Int).Sub(sum, key.OTPKey) res.Mod(res, dm.Params.Bound) return res, nil } ================================================ FILE: innerprod/simple/ddh_multi_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package simple_test import ( "math/big" "testing" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/innerprod/simple" "github.com/fentec-project/gofe/sample" "github.com/stretchr/testify/assert" ) func testSimpleMultiDDHFromParam(t *testing.T, param ddhTestParam) { // choose meta-parameters for the scheme numOfSlots := 2 l := 3 bound := big.NewInt(1000) sampler := sample.NewUniform(bound) // build the central authority for the scheme var multiDDH *simple.DDHMulti var err error if param.precomputed { // modulusLength defines the security of the scheme, the higher the better multiDDH, err = simple.NewDDHMultiPrecomp(numOfSlots, l, param.modulusLength, bound) } else { multiDDH, err = simple.NewDDHMulti(numOfSlots, l, param.modulusLength, bound) } if err != nil { t.Fatalf("Failed to initialize multi input inner product: %v", err) } // the central authority generates keys for all the clients pubKey, secKey, err := multiDDH.GenerateMasterKeys() if err != nil { t.Fatalf("Error during keys generation: %v", err) } // pick a matrix that represent the collection of inner-product vectors y_i y, err := data.NewRandomMatrix(numOfSlots, l, sampler) if err != nil { t.Fatalf("Error during matrix generation: %v", err) } // we simulate different clients encrypting which might be on different machines (this means "multi-input"), // ciphertexts are then collected by decryptor and inner-product over vectors from all encryptors is computed clients := make([]*simple.DDHMultiClient, numOfSlots) for i := 0; i < numOfSlots; i++ { clients[i] = simple.NewDDHMultiClient(multiDDH.Params) } // central authority derives the key for the decryptor derivedKey, err := multiDDH.DeriveKey(secKey, y) if err != nil { t.Fatalf("Error during key derivation: %v", err) } // each client encrypts its vector x_i collectedX := make([]data.Vector, numOfSlots) // for checking whether encrypt/decrypt works properly ciphertexts := make([]data.Vector, numOfSlots) for i := 0; i < numOfSlots; i++ { x, err := data.NewRandomVector(l, sampler) // x possessed and chosen by clients[i] if err != nil { t.Fatalf("Error during random vector generation: %v", err) } collectedX[i] = x c, err := clients[i].Encrypt(x, pubKey[i], secKey.OtpKey[i]) if err != nil { t.Fatalf("Error during encryption: %v", err) } ciphertexts[i] = c } xMatrix, err := data.NewMatrix(collectedX) if err != nil { t.Fatalf("Error during collection of vectors to be encrypted: %v", err) } xyCheck, err := xMatrix.Dot(y) xyCheck.Mod(xyCheck, bound) if err != nil { t.Fatalf("Error during inner product calculation: %v", err) } // we simulate the decryptor decryptor := simple.NewDDHMultiFromParams(numOfSlots, multiDDH.Params) ciphertextMatrix, err := data.NewMatrix(ciphertexts) if err != nil { t.Fatalf("Error during collection of ciphertexts: %v", err) } // decryptor decrypts the value xy, err := decryptor.Decrypt(ciphertextMatrix, derivedKey, y) if err != nil { t.Fatalf("Error during decryption: %v", err) } // we check if the decrypted value is correct assert.Equal(t, xy.Cmp(xyCheck), 0, "obtained incorrect inner product") } func TestSimple_MultiDDH(t *testing.T) { params := []ddhTestParam{{name: "random", modulusLength: 512, precomputed: false}, {name: "precomputed", modulusLength: 2048, precomputed: true}} for _, param := range params { t.Run(param.name, func(t *testing.T) { testSimpleMultiDDHFromParam(t, param) }) } } ================================================ FILE: innerprod/simple/ddh_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package simple_test import ( "math/big" "testing" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/innerprod/simple" "github.com/fentec-project/gofe/sample" "github.com/stretchr/testify/assert" ) type ddhTestParam struct { name string modulusLength int precomputed bool } func testSimpleDDHFromParam(t *testing.T, param ddhTestParam) { l := 3 bound := new(big.Int).Exp(big.NewInt(2), big.NewInt(10), nil) sampler := sample.NewUniformRange(new(big.Int).Add(new(big.Int).Neg(bound), big.NewInt(1)), bound) var simpleDDH *simple.DDH var err error if param.precomputed { simpleDDH, err = simple.NewDDHPrecomp(l, param.modulusLength, bound) } else { simpleDDH, err = simple.NewDDH(l, param.modulusLength, bound) } if err != nil { t.Fatalf("Error during simple inner product creation: %v", err) } masterSecKey, masterPubKey, err := simpleDDH.GenerateMasterKeys() if err != nil { t.Fatalf("Error during master key generation: %v", err) } y, err := data.NewRandomVector(l, sampler) if err != nil { t.Fatalf("Error during random generation: %v", err) } funcKey, err := simpleDDH.DeriveKey(masterSecKey, y) if err != nil { t.Fatalf("Error during key derivation: %v", err) } x, err := data.NewRandomVector(l, sampler) if err != nil { t.Fatalf("Error during random generation: %v", err) } // simulate the instantiation of encryptor (which should be given masterPubKey) encryptor := simple.NewDDHFromParams(simpleDDH.Params) xyCheck, err := x.Dot(y) if err != nil { t.Fatalf("Error during inner product calculation") } ciphertext, err := encryptor.Encrypt(x, masterPubKey) if err != nil { t.Fatalf("Error during encryption: %v", err) } decryptor := simple.NewDDHFromParams(simpleDDH.Params) xy, err := decryptor.Decrypt(ciphertext, funcKey, y) if err != nil { t.Fatalf("Error during decryption: %v", err) } assert.Equal(t, xy, xyCheck, "Original and decrypted values should match") } func TestSimple_DDH(t *testing.T) { params := []ddhTestParam{{name: "random", modulusLength: 512, precomputed: false}, {name: "precomputed", modulusLength: 2048, precomputed: true}} for _, param := range params { t.Run(param.name, func(t *testing.T) { testSimpleDDHFromParam(t, param) }) } } ================================================ FILE: innerprod/simple/doc.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ // Package simple includes simple schemes for functional encryption of // inner products. // // All implementations in this package are based on the reference // paper by Abdalla et. al (see https://eprint.iacr.org/2015/017.pdf). // The reference scheme offers selective security under chosen-plaintext // attacks (s-IND-CPA security). // // The reference scheme is public key, which means that no master secret // key is required for the encryption. // // For instantiation from the decisional Diffie-Hellman assumption // (DDH), see struct DDH (and its multi-input variant DDHMulti, which // is a secret key scheme, because a part of the secret key is required // for the encryption). // // For instantiation from learning with errors (LWE), see // structs LWE and RingLWE. package simple ================================================ FILE: innerprod/simple/lwe.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package simple import ( "math/big" "crypto/rand" "math" "math/bits" "fmt" "github.com/fentec-project/gofe/data" gofe "github.com/fentec-project/gofe/internal" "github.com/fentec-project/gofe/sample" "github.com/pkg/errors" ) // LWEParams represents parameters for the simple LWE scheme. type LWEParams struct { L int // Length of data vectors for inner product N int // Main security parameters of the scheme M int // Number of rows (samples) for the LWE problem BoundX *big.Int // Bound for input vector coordinates (for x) BoundY *big.Int // Bound for inner product vector coordinates (for y) P *big.Int // Modulus for message space Q *big.Int // Modulus for ciphertext and keys SigmaQ *big.Float // standard deviation for the noise terms LWE LSigma *big.Int // precomputed LSigma = SigmaQ / (1/2log(2)) needed for sampling // Matrix A of dimensions M*N is a public parameter of the scheme A data.Matrix } // LWE represents a scheme instantiated from the LWE assumption, // based on the LWE variant by // Abdalla, Bourse, De Caro, and Pointchev: // "Simple Functional Encryption Schemes for Inner Products". type LWE struct { Params *LWEParams } // NewLWE configures a new instance of the scheme. // It accepts the length of input vectors l, bound for coordinates of // input vectors x and y, the main security parameters n and m, // modulus for input data p, and modulus for ciphertext and keys q. // Security parameters are generated so that they satisfy theoretical // bounds provided in the phd thesis Functional Encryption for // Inner-Product Evaluations, see Section 8.3.1 in // https://www.di.ens.fr/~fbourse/publications/Thesis.pdf // Note that this is a prototype implementation and should not be // used in production before security testing against various // known attacks has been performed. Unfortunately, no such (theoretical) // evaluation exists yet in the literature. // // It returns an error in case public parameters of the scheme could // not be generated. func NewLWE(l int, boundX, boundY *big.Int, n int) (*LWE, error) { // generate parameters // p > boundX * boundY * l * 2 nBitsP := boundX.BitLen() + boundY.BitLen() + bits.Len(uint(l)) + 2 p, err := rand.Prime(rand.Reader, nBitsP) if err != nil { return nil, errors.Wrap(err, "cannot generate public parameters") } pF := new(big.Float).SetInt(p) boundXF := new(big.Float).SetInt(boundX) boundYF := new(big.Float).SetInt(boundY) val := new(big.Float).Mul(boundXF, big.NewFloat(math.Sqrt(float64(l)))) val.Add(val, big.NewFloat(1)) x := new(big.Float).Mul(val, pF) x.Mul(x, boundYF) x.Mul(x, big.NewFloat(float64(8*n)*math.Sqrt(float64(n+l+1)))) xSqrt := new(big.Float).Sqrt(x) x.Mul(x, xSqrt) xI, _ := x.Int(nil) nBitsQ := xI.BitLen() + 1 q, err := rand.Prime(rand.Reader, nBitsQ) if err != nil { return nil, errors.Wrap(err, "cannot generate public parameters") } m := (n+l+1)*nBitsQ + 2*n + 1 sigma := new(big.Float) sigma.SetPrec(uint(n)) sigma.Quo(big.NewFloat(1/(2*math.Sqrt(float64(2*l*m*n)))), pF) sigma.Quo(sigma, boundYF) qF := new(big.Float).SetInt(q) sigmaQ := new(big.Float).Mul(sigma, qF) // to sample with NormalDoubleConstant sigmaQ must be // a multiple of sample.SigmaCDT = sqrt(1/2ln(2)), hence we make // it such lSigmaF := new(big.Float).Quo(sigmaQ, sample.SigmaCDT) lSigma, _ := lSigmaF.Int(nil) lSigma.Add(lSigma, big.NewInt(1)) lSigmaF.SetInt(lSigma) sigmaQ.Mul(sample.SigmaCDT, lSigmaF) // sanity check if the parameters satisfy theoretical bounds val.Quo(sigmaQ, val) if val.Cmp(big.NewFloat(2*math.Sqrt(float64(n)))) < 1 { return nil, fmt.Errorf("parameters generation faliled, sigmaQ too small") } // generate a random matrix A, err := data.NewRandomMatrix(m, n, sample.NewUniform(q)) if err != nil { return nil, errors.Wrap(err, "cannot generate public parameters") } return &LWE{ Params: &LWEParams{ L: l, BoundX: boundX, BoundY: boundY, N: n, M: m, P: p, Q: q, A: A, SigmaQ: sigmaQ, LSigma: lSigma, }, }, nil } // GenerateSecretKey generates a secret key for the scheme. // The key is represented by a matrix with dimensions n*l whose // elements are random values from the interval [0, q). // // In case secret key could not be generated, it returns an error. func (s *LWE) GenerateSecretKey() (data.Matrix, error) { return data.NewRandomMatrix(s.Params.N, s.Params.L, sample.NewUniform(s.Params.Q)) } // GeneratePublicKey accepts a secret key SK, standard deviation sigma. // It generates a public key PK for the scheme. Public key is a matrix // of m*l elements. // // In case of a malformed secret key the function returns an error. func (s *LWE) GeneratePublicKey(SK data.Matrix) (data.Matrix, error) { if !SK.CheckDims(s.Params.N, s.Params.L) { return nil, gofe.ErrMalformedSecKey } // Initialize and fill noise matrix E with m*l samples sampler := sample.NewNormalDoubleConstant(s.Params.LSigma) E, err := data.NewRandomMatrix(s.Params.M, s.Params.L, sampler) if err != nil { return nil, errors.Wrap(err, "error generating public key") } // Calculate public key as PK = (A * SK + E) % q // we ignore error checks because they errors could only arise if SK // was not of the proper form, but we validated it at the beginning PK, _ := s.Params.A.Mul(SK) PK = PK.Mod(s.Params.Q) PK, _ = PK.Add(E) PK = PK.Mod(s.Params.Q) return PK, nil } // DeriveKey accepts input vector y and master secret key SK, and derives a // functional encryption key. // // In case of malformed secret key or input vector that violates the configured // bound, it returns an error. func (s *LWE) DeriveKey(y data.Vector, SK data.Matrix) (data.Vector, error) { if err := y.CheckBound(s.Params.BoundY); err != nil { return nil, err } if !SK.CheckDims(s.Params.N, s.Params.L) { return nil, gofe.ErrMalformedSecKey } // Secret key is a linear combination of input vector y // and master secret key SK. skY, err := SK.MulVec(y) if err != nil { return nil, gofe.ErrMalformedInput } skY = skY.Mod(s.Params.Q) return skY, nil } // Encrypt encrypts vector x using public key PK. // It returns the resulting ciphertext vector. In case of malformed // public key or input vector that violates the configured bound, // it returns an error. func (s *LWE) Encrypt(x data.Vector, PK data.Matrix) (data.Vector, error) { if err := x.CheckBound(s.Params.BoundX); err != nil { return nil, err } if !PK.CheckDims(s.Params.M, s.Params.L) { return nil, gofe.ErrMalformedPubKey } if len(x) != s.Params.L { return nil, gofe.ErrMalformedInput } // Create a random vector comprised of m 0s and 1s r, err := data.NewRandomVector(s.Params.M, sample.NewBit()) if err != nil { return nil, errors.Wrap(err, "error in encrypt") } // Ciphertext vectors will be composed of two vectors: ct0 and ctLast. // ct0 ... a vector comprising the first n elements of the cipher // ctLast ... a vector comprising the last l elements of the cipher // ct0 = A(transposed) * r ATrans := s.Params.A.Transpose() ct0, _ := ATrans.MulVec(r) // Calculate coordinates ct_last_i = + t(xi) mod q // We can obtain the vector of dot products as PK(transposed) * r // Function t(x) is denoted as the center function PKTrans := PK.Transpose() ctLast, _ := PKTrans.MulVec(r) t := s.center(x) ctLast = ctLast.Add(t) ctLast = ctLast.Mod(s.Params.Q) // Construct the final ciphertext vector by joining both parts return append(ct0, ctLast...), nil } // Calculates the center function t(x) = floor(x*q/p) % q for a vector x. func (s *LWE) center(v data.Vector) data.Vector { return v.Apply(func(x *big.Int) *big.Int { t := new(big.Int) t.Mul(x, s.Params.Q) t.Div(t, s.Params.P) t.Mod(t, s.Params.Q) return t }) } // Decrypt accepts an encrypted vector ct, functional encryption key skY, // and plaintext vector y. It returns the inner product of x and y. // If decryption failed (for instance with input data that violates the // configured bound or malformed ciphertext or keys), error is returned. func (s *LWE) Decrypt(ct, skY, y data.Vector) (*big.Int, error) { if err := y.CheckBound(s.Params.BoundY); err != nil { return nil, err } if len(skY) != s.Params.N { return nil, gofe.ErrMalformedDecKey } if len(y) != s.Params.L { return nil, gofe.ErrMalformedInput } // Break down the ciphertext vector into // ct0 which holds first n elements of the cipher, and // ctLast which holds last n elements of the cipher if len(ct) != s.Params.N+s.Params.L { return nil, gofe.ErrMalformedCipher } ct0 := ct[:s.Params.N] ctLast := ct[s.Params.N:] // Calculate d = - yDotCtLast, _ := y.Dot(ctLast) yDotCtLast.Mod(yDotCtLast, s.Params.Q) ct0DotSkY, _ := ct0.Dot(skY) ct0DotSkY.Mod(ct0DotSkY, s.Params.Q) halfQ := new(big.Int).Div(s.Params.Q, big.NewInt(2)) // d will hold the decrypted message d := new(big.Int).Sub(yDotCtLast, ct0DotSkY) d.Mod(d, s.Params.Q) // in case d > q/2 the result will be negative if d.Cmp(halfQ) == 1 { d.Sub(d, s.Params.Q) } d.Mul(d, s.Params.P) d.Add(d, halfQ) d.Div(d, s.Params.Q) return d, nil } ================================================ FILE: innerprod/simple/lwe_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package simple_test import ( "math/big" "testing" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/innerprod/simple" "github.com/fentec-project/gofe/sample" "github.com/stretchr/testify/assert" ) func TestSimple_LWE(t *testing.T) { l := 4 n := 128 b := big.NewInt(10000) x, y, xy := testVectorData(l, b, b) emptyVec := data.Vector{} emptyMat := data.Matrix{} simpleLWE, err := simple.NewLWE(l, b, b, n) assert.NoError(t, err) SK, err := simpleLWE.GenerateSecretKey() assert.NoError(t, err) _, err = simpleLWE.GeneratePublicKey(emptyMat) assert.Error(t, err) PK, err := simpleLWE.GeneratePublicKey(SK) assert.NoError(t, err) _, err = simpleLWE.DeriveKey(emptyVec, SK) assert.Error(t, err) _, err = simpleLWE.DeriveKey(y, emptyMat) assert.Error(t, err) skY, err := simpleLWE.DeriveKey(y, SK) assert.NoError(t, err) _, err = simpleLWE.Encrypt(emptyVec, PK) assert.Error(t, err) _, err = simpleLWE.Encrypt(x, emptyMat) assert.Error(t, err) cipher, err := simpleLWE.Encrypt(x, PK) assert.NoError(t, err) _, err = simpleLWE.Decrypt(emptyVec, skY, y) assert.Error(t, err) _, err = simpleLWE.Decrypt(cipher, emptyVec, y) assert.Error(t, err) _, err = simpleLWE.Decrypt(cipher, skY, emptyVec) assert.Error(t, err) xyDecrypted, err := simpleLWE.Decrypt(cipher, skY, y) assert.NoError(t, err) assert.Equal(t, xy.Cmp(xyDecrypted), 0, "obtained incorrect inner product") } // testVectorData returns random vectors x, y, each containing // elements up to the respective bound. // It also returns the dot product of the vectors. func testVectorData(len int, boundX, boundY *big.Int) (data.Vector, data.Vector, *big.Int) { samplerX := sample.NewUniformRange(new(big.Int).Neg(boundX), boundX) samplerY := sample.NewUniformRange(new(big.Int).Neg(boundY), boundY) x, _ := data.NewRandomVector(len, samplerX) y, _ := data.NewRandomVector(len, samplerY) xy, _ := x.Dot(y) return x, y, xy } ================================================ FILE: innerprod/simple/ringlwe.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package simple import ( gofe "github.com/fentec-project/gofe/internal" "math" "math/big" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/sample" "github.com/pkg/errors" ) // RingLWEParams represents parameters for the ring LWE scheme. type RingLWEParams struct { L int // Length of data vectors for inner product // Main security parameters of the scheme N int // Settings for discrete gaussian sampler Sigma1 *big.Float // standard deviation Sigma2 *big.Float // standard deviation Sigma3 *big.Float // standard deviation BoundX *big.Int // upper bound for coordinates of input vectors BoundY *big.Int // upper bound for coordinates of inner-product vectors P *big.Int // bound for the resulting inner product Q *big.Int // modulus for ciphertext and keys // A is a vector with N coordinates. // It represents a random polynomial for the scheme. A data.Vector } // RingLWE represents a FE scheme instantiated from the ringLWE assumption. It // allows to encrypt a matrix X and derive a FE based on a vector y, so that // one can decrypt y^T * X and nothing else. This can be seen as a SIMD version // of a simple inner product scheme, since multiple vectors (columns of X) can be // multiplied with y at the same time. // It is based on // Bermudo Mera, Karmakar, Marc, and Soleimanian: // "Efficient Lattice-Based Inner-Product Functional Encryption", // see https://eprint.iacr.org/2021/046. type RingLWE struct { Params *RingLWEParams } // NewRingLWE configures a new instance of the scheme. // It accepts a security parameter sec, the length of input vectors l, // bound for coordinates of input vectors x and y. It generates all the // parameters needed to have a scheme with at least sec bits of security // by using all the bounds derived in the paper https://eprint.iacr.org/2021/046, // as well as having the parameters secure against so called primal attack // on LWE. func NewRingLWE(sec, l int, boundX, boundY *big.Int) (*RingLWE, error) { K := new(big.Int).Mul(boundX, boundY) K.Mul(K, big.NewInt(int64(2*l))) kappa := big.NewFloat(float64(sec)) kappaSqrt := new(big.Float).Sqrt(kappa) sigma := big.NewFloat(1) sigma1 := new(big.Float).Mul(big.NewFloat(math.Sqrt(float64(4*l))), sigma) sigma1.Mul(sigma1, new(big.Float).SetInt(boundX)) var q *big.Int var sigma2, sigma3 *big.Float var safe bool var n int boundOfB := float64(sec) / 0.265 for pow := 6; pow < 20; pow++ { n = 1 << uint(pow) sigma2 = new(big.Float).Mul(big.NewFloat(math.Sqrt(float64(2*(l+2)*n*n))), sigma) sigma2.Mul(sigma2, sigma1) sigma2.Mul(sigma2, kappaSqrt) sigma3 = new(big.Float).Mul(sigma2, big.NewFloat(math.Sqrt(float64(2)))) qFloat1 := new(big.Float).Mul(sigma1, sigma2) qFloat1.Mul(qFloat1, kappa) qFloat1.Mul(qFloat1, big.NewFloat(float64(2*n))) qFloat2 := new(big.Float).Mul(kappaSqrt, sigma3) qFloat := new(big.Float).Add(qFloat1, qFloat2) qFloat.Mul(qFloat, new(big.Float).SetInt(boundY)) qFloat.Mul(qFloat, big.NewFloat(float64(2*l))) q, _ = qFloat.Int(nil) q.Mul(q, K) qF := new(big.Float).SetInt(q) qFF, _ := qF.Float64() sigmaPrimeQF, _ := sigma.Float64() safe = true for b := float64(50); b <= boundOfB; b = b + 1 { for m := int(math.Max(1, b-float64(n))); m < 3*n; m++ { delta := math.Pow(math.Pow(math.Pi*b, 1/b)*b/(2*math.Pi*math.E), 1./(2.*b-2.)) left := sigmaPrimeQF * math.Sqrt(b) d := n + m right := math.Pow(delta, 2*b-float64(d)-1) * math.Pow(qFF, float64(m)/float64(d)) if left < right { safe = false break } } if !safe { break } } if safe { break } } randVec, err := data.NewRandomVector(n, sample.NewUniform(q)) if err != nil { return nil, errors.Wrap(err, "cannot generate random polynomial") } return &RingLWE{ Params: &RingLWEParams{ L: l, N: n, BoundX: boundX, BoundY: boundY, P: K, Q: q, Sigma1: sigma1, Sigma2: sigma2, Sigma3: sigma3, A: randVec, }, }, nil } // GenerateSecretKey generates a secret key for the scheme. // The key is a matrix of l*n small elements sampled from // Discrete Gaussian distribution. // // In case secret key could not be generated, it returns an error. func (s *RingLWE) GenerateSecretKey() (data.Matrix, error) { lSigmaF := new(big.Float).Quo(s.Params.Sigma1, sample.SigmaCDT) lSigma, _ := lSigmaF.Int(nil) sampler := sample.NewNormalDoubleConstant(lSigma) return data.NewRandomMatrix(s.Params.L, s.Params.N, sampler) } // GeneratePublicKey accepts a master secret key SK and generates a // corresponding master public key. // Public key is a matrix of l*n elements. // In case of a malformed secret key the function returns an error. func (s *RingLWE) GeneratePublicKey(SK data.Matrix) (data.Matrix, error) { if !SK.CheckDims(s.Params.L, s.Params.N) { return nil, gofe.ErrMalformedPubKey } // Generate noise matrix // Elements are sampled from the same distribution as the secret key S. lSigmaF := new(big.Float).Quo(s.Params.Sigma1, sample.SigmaCDT) lSigma, _ := lSigmaF.Int(nil) sampler := sample.NewNormalDoubleConstant(lSigma) E, err := data.NewRandomMatrix(s.Params.L, s.Params.N, sampler) if err != nil { return nil, errors.Wrap(err, "public key generation failed") } // Calculate public key PK row by row as PKi = (a * SKi + Ei) % q. // Multiplication and addition are in the ring of polynomials PK := make(data.Matrix, s.Params.L) for i := 0; i < PK.Rows(); i++ { pkI, _ := SK[i].MulAsPolyInRing(s.Params.A) pkI = pkI.Add(E[i]) PK[i] = pkI } PK = PK.Mod(s.Params.Q) return PK, nil } // DeriveKey accepts input vector y and master secret key SK, and derives a // functional encryption key. // In case of malformed secret key or input vector that violates the // configured bound, it returns an error. func (s *RingLWE) DeriveKey(y data.Vector, SK data.Matrix) (data.Vector, error) { if err := y.CheckBound(s.Params.BoundY); err != nil { return nil, err } if !SK.CheckDims(s.Params.L, s.Params.N) { return nil, gofe.ErrMalformedSecKey } // Secret key is a linear combination of input vector y and master secret keys. SKTrans := SK.Transpose() skY, err := SKTrans.MulVec(y) if err != nil { return nil, gofe.ErrMalformedInput } skY = skY.Mod(s.Params.Q) return skY, nil } // Calculates the center function t(x) = floor(x*q/p) % q for a matrix X and // place it in a bigger matrix. func (s *RingLWE) center(X data.Matrix) data.Matrix { ret := data.NewConstantMatrix(s.Params.L, s.Params.N, big.NewInt(0)) for i := 0; i < X.Rows(); i++ { for j := 0; j < X.Cols(); j++ { ret[i][j].Mul(X[i][j], s.Params.Q) ret[i][j].Div(ret[i][j], s.Params.P) ret[i][j].Mod(ret[i][j], s.Params.Q) } } return ret } // RingLWECipher is functional encryption key for DDH Scheme. type RingLWECipher struct { Ct0 data.Matrix Ct1 data.Vector K int } // Encrypt encrypts matrix X using public key PK. // It returns the resulting ciphertext matrix. In case of malformed // public key or input matrix that violates the configured bound, // it returns an error. // //The resulting ciphertext has dimensions (l + 1) * n. func (s *RingLWE) Encrypt(X data.Matrix, PK data.Matrix) (*RingLWECipher, error) { if err := X.CheckBound(s.Params.BoundX); err != nil { return nil, err } if !PK.CheckDims(s.Params.L, s.Params.N) { return nil, gofe.ErrMalformedPubKey } if !(X.Rows() == s.Params.L && X.Cols() <= s.Params.N) { return nil, gofe.ErrMalformedInput } // Create a small random vector r lSigma2F := new(big.Float).Quo(s.Params.Sigma2, sample.SigmaCDT) lSigma2, _ := lSigma2F.Int(nil) sampler2 := sample.NewNormalDoubleConstant(lSigma2) r, err := data.NewRandomVector(s.Params.N, sampler2) if err != nil { return nil, errors.Wrap(err, "error in encrypt") } // Create noise matrix E to secure the encryption lSigma3F := new(big.Float).Quo(s.Params.Sigma3, sample.SigmaCDT) lSigma3, _ := lSigma3F.Int(nil) sampler3 := sample.NewNormalDoubleConstant(lSigma3) E, err := data.NewRandomMatrix(s.Params.L, s.Params.N, sampler3) if err != nil { return nil, errors.Wrap(err, "error in encrypt") } // Calculate cipher CT row by row as CTi = (PKi * r + Ei) % q. // Multiplication and addition are in the ring of polynomials. CT0 := make(data.Matrix, s.Params.L) for i := 0; i < CT0.Rows(); i++ { CT0i, _ := PK[i].MulAsPolyInRing(r) CT0i = CT0i.Add(E[i]) CT0[i] = CT0i } CT0 = CT0.Mod(s.Params.Q) // Include the message X in the encryption T := s.center(X) CT0, _ = CT0.Add(T) CT0 = CT0.Mod(s.Params.Q) // Construct the last row of the cipher ct1, _ := s.Params.A.MulAsPolyInRing(r) e, err := data.NewRandomVector(s.Params.N, sampler2) if err != nil { return nil, errors.Wrap(err, "error in encrypt") } ct1 = ct1.Add(e) ct1 = ct1.Mod(s.Params.Q) res := &RingLWECipher{Ct0: CT0, Ct1: ct1, K: X.Cols()} return res, nil } // Decrypt accepts a ciphertext CT, secret key skY, and plaintext // vector y, and returns a vector of inner products of X's rows and y. // If decryption failed (for instance with input data that violates the // configured bound or malformed ciphertext or keys), error is returned. func (s *RingLWE) Decrypt(CT *RingLWECipher, skY, y data.Vector) (data.Vector, error) { if err := y.CheckBound(s.Params.BoundY); err != nil { return nil, err } if len(skY) != s.Params.N { return nil, gofe.ErrMalformedDecKey } if len(y) != s.Params.L { return nil, gofe.ErrMalformedInput } if !CT.Ct0.CheckDims(s.Params.L, s.Params.N) || len(CT.Ct1) != s.Params.N { return nil, gofe.ErrMalformedCipher } CT0 := CT.Ct0 ct1 := CT.Ct1 CT0Trans := CT0.Transpose() CT0TransMulY, _ := CT0Trans.MulVec(y) CT0TransMulY = CT0TransMulY.Mod(s.Params.Q) ct1MulSkY, _ := ct1.MulAsPolyInRing(skY) ct1MulSkY = ct1MulSkY.Apply(func(x *big.Int) *big.Int { return new(big.Int).Neg(x) }) d := CT0TransMulY.Add(ct1MulSkY) d = d.Mod(s.Params.Q) halfQ := new(big.Int).Div(s.Params.Q, big.NewInt(2)) d = d.Apply(func(x *big.Int) *big.Int { if x.Cmp(halfQ) == 1 { x.Sub(x, s.Params.Q) } x.Mul(x, s.Params.P) x.Add(x, halfQ) x.Div(x, s.Params.Q) return x }) return d[:CT.K], nil } ================================================ FILE: innerprod/simple/ringlwe_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package simple_test import ( "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/sample" "math/big" "testing" "github.com/fentec-project/gofe/innerprod/simple" "github.com/stretchr/testify/assert" ) func TestSimple_RingLWE(t *testing.T) { // choose meta-parameters l := 30 // l-dimensional vectors sec := 75 // min bits of security bx := big.NewInt(2) // bound for the input coordinates by := big.NewInt(2) // bound for the inner-product coordinates // setup a ringLWE scheme ringLWE, err := simple.NewRingLWE(sec, l, bx, by) assert.NoError(t, err) // sample an input and an inner-product vector sampler := sample.NewUniformRange(new(big.Int).Neg(bx), bx) y, _ := data.NewRandomVector(l, sampler) dimX := ringLWE.Params.N / 2 X, _ := data.NewRandomMatrix(l, dimX, sampler) xy, _ := X.Transpose().MulVec(y) // generate a master secret key SK, err := ringLWE.GenerateSecretKey() assert.NoError(t, err) // generate a public key PK, err := ringLWE.GeneratePublicKey(SK) assert.NoError(t, err) // check if errors are raised if the input is of the wrong form emptyVec := data.Vector{} emptyMat := data.Matrix{} _, err = ringLWE.GeneratePublicKey(emptyMat) assert.Error(t, err) _, err = ringLWE.DeriveKey(emptyVec, SK) assert.Error(t, err) _, err = ringLWE.DeriveKey(y, emptyMat) assert.Error(t, err) // derive FE key with respect to y skY, err := ringLWE.DeriveKey(y, SK) assert.NoError(t, err) // encrypt a matrix X cipher, err := ringLWE.Encrypt(X, PK) assert.NoError(t, err) // check if errors are raised if the input is of the wrong form _, err = ringLWE.Encrypt(emptyMat, PK) assert.Error(t, err) _, err = ringLWE.Encrypt(X, emptyMat) assert.Error(t, err) // decrypt the product y^T * X using the derived key xyDecrypted, err := ringLWE.Decrypt(cipher, skY, y) assert.NoError(t, err) // check the result for i:=0; i < dimX; i++ { assert.Equal(t, xy[i].Cmp(xyDecrypted[i]), 0, "obtained incorrect inner product") } } ================================================ FILE: internal/dlog/brute_force.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package dlog import ( "fmt" "math/big" "github.com/fentec-project/bn256" ) // Simply brute-forces all possible options. func bruteForce(h, g, p, bound *big.Int) (*big.Int, error) { if bound == nil { bound = new(big.Int).Sub(p, big.NewInt(1)) } for i := big.NewInt(0); i.Cmp(bound) < 0; i.Add(i, big.NewInt(1)) { if new(big.Int).Exp(g, i, p).Cmp(h) == 0 { return i, nil } } return nil, fmt.Errorf("failed to find discrete logarithm within bound") } // Simply brute-forces all possible options to compute dlog in BN256 GT group. func bruteForceBN256(h, g *bn256.GT, bound *big.Int) (*big.Int, error) { if bound == nil { bound = bn256.Order } for i := big.NewInt(0); i.Cmp(bound) <= 0; i.Add(i, big.NewInt(1)) { t := new(bn256.GT).ScalarMult(g, i) if t.String() == h.String() { return i, nil } } return nil, fmt.Errorf("failed to find discrete logarithm within bound") } ================================================ FILE: internal/dlog/brute_force_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package dlog import ( "math/big" "testing" "github.com/fentec-project/bn256" "github.com/fentec-project/gofe/internal/keygen" "github.com/fentec-project/gofe/sample" "github.com/stretchr/testify/assert" ) func TestBruteForceBN256(t *testing.T) { bound := big.NewInt(1000) sampler := sample.NewUniform(bound) xCheck, err := sampler.Sample() if err != nil { t.Fatalf("error in random value generation: %v", err) } g1gen := new(bn256.G1).ScalarBaseMult(big.NewInt(1)) g2gen := new(bn256.G2).ScalarBaseMult(big.NewInt(1)) g := bn256.Pair(g1gen, g2gen) h := new(bn256.GT).ScalarMult(g, xCheck) x, err := bruteForceBN256(h, g, bound) if err != nil { t.Fatalf("error in brute force algorithm: %v", err) } assert.Equal(t, xCheck.Cmp(x), 0, "obtained incorrect result") } func TestBruteForce(t *testing.T) { bound := big.NewInt(1000) sampler := sample.NewUniform(bound) xCheck, err := sampler.Sample() if err != nil { t.Fatalf("error in random value generation: %v", err) } key, err := keygen.NewElGamal(20) if err != nil { t.Fatalf("error in parameters generation: %v", err) } h := new(big.Int).Exp(key.G, xCheck, key.P) x, err := bruteForce(h, key.G, key.P, bound) if err != nil { t.Fatalf("error in brute force algorithm: %v", err) } assert.Equal(t, xCheck.Cmp(x), 0, "obtained incorrect result") } ================================================ FILE: internal/dlog/calc.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package dlog import ( "crypto/sha1" "fmt" "math/big" "github.com/fentec-project/bn256" ) // MaxBound limits the interval of values that are checked when // computing discrete logarithms. It prevents time and memory // exhaustive computation for practical purposes. // If Calc is configured to use a boundary value > MaxBound, // it will be automatically adjusted to MaxBound. var MaxBound = new(big.Int).Exp(big.NewInt(2), big.NewInt(48), nil) // Calc represents a discrete logarithm calculator. type Calc struct{} // NewCalc generates a new discrete logarithm calculator. func NewCalc() *Calc { return &Calc{} } // CalcZp represents a calculator for discrete logarithms // that operates in the Zp group of integers modulo prime p. type CalcZp struct { p *big.Int bound *big.Int m *big.Int neg bool } // InZp builds parameters needed to calculate a discrete // logarithm in Z_p group. func (*Calc) InZp(p, order *big.Int) (*CalcZp, error) { one := big.NewInt(1) var bound *big.Int if p == nil { return nil, fmt.Errorf("group modulus p cannot be nil") } if order == nil { if !p.ProbablyPrime(20) { return nil, fmt.Errorf("group modulus p must be prime") } bound = new(big.Int).Sub(p, one) } else { bound = order } m := new(big.Int).Sqrt(bound) m.Add(m, one) return &CalcZp{ p: p, bound: bound, m: m, neg: false, }, nil } // WithBound sets a bound for the calculator of the discrete logarithm. func (c *CalcZp) WithBound(bound *big.Int) *CalcZp { if bound != nil && bound.Cmp(MaxBound) < 0 && bound.Sign() > 0 { m := new(big.Int).Sqrt(bound) m.Add(m, big.NewInt(1)) return &CalcZp{ bound: bound, m: m, p: c.p, neg: c.neg, } } return c } // WithNeg sets that the result should be searched also among // negative integers. func (c *CalcZp) WithNeg() *CalcZp { return &CalcZp{ bound: c.bound, m: c.m, p: c.p, neg: true, } } // BabyStepGiantStep uses the baby-step giant-step method to // compute the discrete logarithm in the Zp group. If c.neg is // set to true it searches for the answer within [-bound, bound]. // It does so by running two goroutines, one for negative // answers and one for positive. If c.neg is set to false // only one goroutine is started, searching for the answer // within [0, bound]. func (c *CalcZp) BabyStepGiantStep(h, g *big.Int) (*big.Int, error) { // create goroutines calculating positive and possibly negative // result if c.neg is set to true retChan := make(chan *big.Int) errChan := make(chan error) go c.runBabyStepGiantStepIterative(h, g, retChan, errChan) if c.neg { gInv := new(big.Int).ModInverse(g, c.p) go c.runBabyStepGiantStepIterative(h, gInv, retChan, errChan) } // catch a value when the first routine finishes ret := <-retChan err := <-errChan // prevent the situation when one routine exhausted all possibilities // before the second found the solution if c.neg && err != nil { ret = <-retChan err = <-errChan } // if both routines give an error, return an error if err != nil { return nil, err } // based on ret decide which routine gave the answer, thus if // answer is negative if c.neg && h.Cmp(new(big.Int).Exp(g, ret, c.p)) != 0 { ret.Neg(ret) } return ret, nil } // runBabyStepGiantStep implements the baby-step giant-step method to // compute the discrete logarithm in the Zp group. It is meant to be run // as a goroutine. // // The function searches for x, where h = g^x mod p. If the solution was not found // within the provided bound, it returns an error. func (c *CalcZp) runBabyStepGiantStep(h, g *big.Int, retChan chan *big.Int, errChan chan error) { one := big.NewInt(1) // big.Int cannot be a key, thus we use a stringified bytes representation of the integer T := make(map[string]*big.Int) x := big.NewInt(1) // remainders (r) for i := big.NewInt(0); i.Cmp(c.m) < 0; i.Add(i, one) { // important: insert a copy of i into the map as i is mutated each loop T[string(x.Bytes())] = new(big.Int).Set(i) x = new(big.Int).Mod(new(big.Int).Mul(x, g), c.p) } // g^-m z := new(big.Int).ModInverse(g, c.p) z.Exp(z, c.m, c.p) x = new(big.Int).Set(h) for i := big.NewInt(0); i.Cmp(c.m) < 0; i.Add(i, one) { if e, ok := T[string(x.Bytes())]; ok { retChan <- new(big.Int).Add(new(big.Int).Mul(i, c.m), e) errChan <- nil return } x = new(big.Int).Mod(new(big.Int).Mul(x, z), c.p) } retChan <- nil errChan <- fmt.Errorf("failed to find the discrete logarithm within bound " + c.bound.String()) } // runBabyStepGiantStepIterative implements the baby-step giant-step method to // compute the discrete logarithm in the Zp group. It is meant to be run // as a goroutine. // // The function searches for x, where h = g^x mod p. If the solution was not found // within the provided bound, it returns an error. In contrast to the usual // implementation of the method, this one proceeds iteratively, meaning that // smaller the solution is, faster the algorithm finishes. func (c *CalcZp) runBabyStepGiantStepIterative(h, g *big.Int, retChan chan *big.Int, errChan chan error) { one := big.NewInt(1) two := big.NewInt(2) // big.Int cannot be a key, thus we use a stringified bytes representation of the integer T := make(map[string]*big.Int) // prepare values for the loop x := big.NewInt(1) y := new(big.Int).Set(h) z := new(big.Int).ModInverse(g, c.p) z.Exp(z, two, c.p) bits := int64(c.m.BitLen()) T[string(x.Bytes())] = big.NewInt(0) x.Mod(x.Mul(x, g), c.p) j := big.NewInt(0) giantStep := new(big.Int) bound := new(big.Int) for i := int64(0); i < bits; i++ { // iteratively increasing giant step up to maximal value c.m giantStep.Exp(two, big.NewInt(i+1), nil) if giantStep.Cmp(c.m) > 0 { giantStep.Set(c.m) z.ModInverse(g, c.p) z.Exp(z, c.m, c.p) } // for the selected giant step, add all the needed small steps for k := new(big.Int).Exp(two, big.NewInt(i), nil); k.Cmp(giantStep) < 0; k.Add(k, one) { T[string(x.Bytes())] = new(big.Int).Set(k) x = x.Mod(x.Mul(x, g), c.p) } // make giant steps and search for the solution bound.Exp(two, big.NewInt(2*(i+1)), nil) for ; j.Cmp(bound) < 0; j.Add(j, giantStep) { if e, ok := T[string(y.Bytes())]; ok { retChan <- new(big.Int).Add(j, e) errChan <- nil return } y.Mod(y.Mul(y, z), c.p) } z.Mul(z, z) z.Mod(z, c.p) } retChan <- nil errChan <- fmt.Errorf("failed to find the discrete logarithm within bound") } // CalcBN256 represents a calculator for discrete logarithms // that operates in the BN256 group. type CalcBN256 struct { bound *big.Int m *big.Int Precomp map[string]*big.Int precompMaxBits int neg bool } // InBN256 builds parameters needed to calculate a discrete // logarithm in a pairing BN256 group. func (*Calc) InBN256() *CalcBN256 { m := new(big.Int).Sqrt(MaxBound) m.Add(m, big.NewInt(1)) return &CalcBN256{ bound: MaxBound, m: m, neg: false, } } // WithBound sets a bound for the calculator of the discrete logarithm. func (c *CalcBN256) WithBound(bound *big.Int) *CalcBN256 { if bound != nil && bound.Cmp(MaxBound) < 0 { m := new(big.Int).Sqrt(bound) m.Add(m, big.NewInt(1)) return &CalcBN256{ bound: bound, m: m, Precomp: c.Precomp, precompMaxBits: c.precompMaxBits, neg: c.neg, } } return c } // WithNeg sets that the result should be searched also among // negative integers. func (c *CalcBN256) WithNeg() *CalcBN256 { return &CalcBN256{ bound: c.bound, m: c.m, Precomp: c.Precomp, precompMaxBits: c.precompMaxBits, neg: true, } } // Precompute precomputes small steps for the discrete logarithm // search. The resulting precomputation table is of size 2^maxBits. func (c *CalcBN256) Precompute(maxBits int) error { if maxBits < 2 { return fmt.Errorf("maxBits should be at least 1") } g := new(bn256.GT).ScalarBaseMult(big.NewInt(1)) one := big.NewInt(1) sh := sha1.New() // big.Int cannot be a key, thus we use a stringified bytes representation of the integer T := make(map[string]*big.Int) x := bn256.GetGTOne() for i := big.NewInt(0); i.BitLen() <= maxBits; i.Add(i, one) { sh.Write([]byte(x.String())) T[string(sh.Sum(nil)[:10])] = new(big.Int).Set(i) sh.Reset() x = new(bn256.GT).Add(x, g) } c.Precomp = T c.precompMaxBits = maxBits return nil } // BabyStepGiantStepStd implements the baby-step giant-step method to // compute the discrete logarithm in the BN256.GT group. // // It searches for a solution <= bound. If bound argument is nil, // the bound is automatically set to the hard coded MaxBound. // // The function returns x, where h = g^x in BN256.GT group where operations // are written as multiplications. If the solution was not found // within the provided bound, it returns an error. func (c *CalcBN256) BabyStepGiantStepStd(h, g *bn256.GT) (*big.Int, error) { one := big.NewInt(1) // first part of the method can be reused so we // Precompute it and save it for later if c.Precomp == nil { maxbits := c.m.BitLen() + 1 _ = c.Precompute(maxbits) } // z = g^-m gm := new(bn256.GT).ScalarMult(g, c.m) z := new(bn256.GT).Neg(gm) x := new(bn256.GT).Set(h) for i := big.NewInt(0); i.Cmp(c.m) < 0; i.Add(i, one) { if e, ok := c.Precomp[x.String()]; ok { return new(big.Int).Add(new(big.Int).Mul(i, c.m), e), nil } x.Add(x, z) } return nil, fmt.Errorf("failed to find discrete logarithm within bound") } // BabyStepGiantStep uses the baby-step giant-step method to // compute the discrete logarithm in the BN256.GT group. If c.neg is // set to true it searches for the answer within [-bound, bound]. // It does so by running two goroutines, one for negative // answers and one for positive. If c.neg is set to false // only one goroutine is started, searching for the answer // within [0, bound]. func (c *CalcBN256) BabyStepGiantStep(h, g *bn256.GT) (*big.Int, error) { // create goroutines calculating positive and possibly negative // result if c.neg is set to true retChan := make(chan *big.Int, 2) errChan := make(chan error, 2) quit := make(chan bool, 2) go c.runBabyStepGiantStepIterative(h, g, retChan, errChan, quit) if c.neg { hInv := new(bn256.GT).Neg(h) go c.runBabyStepGiantStepIterative(hInv, g, retChan, errChan, quit) } // catch a value when the first routine finishes ret := <-retChan err := <-errChan // prevent the situation when one routine exhausted all possibilities // before the second found the solution if c.neg && err != nil { ret = <-retChan err = <-errChan } if c.neg { quit <- true } // if both routines give an error, return an error if err != nil { return nil, err } // based on ret decide which routine gave the answer, thus if // answer is negative if c.neg && h.String() != new(bn256.GT).ScalarMult(g, ret).String() { ret.Neg(ret) } return ret, nil } // runBabyStepGiantStepIterative implements the baby-step giant-step method to // compute the discrete logarithm in the BN256.GT group. It is meant to be run // as a goroutine. // // The function searches for x, where h = g^x in BN256.GT group where operations // are written as multiplications. If the solution was not found // within the provided bound, it returns an error. In contrast to the usual // implementation of the method, this one proceeds iteratively, meaning that // smaller the solution is, faster the algorithm finishes. func (c *CalcBN256) runBabyStepGiantStepIterative(h, g *bn256.GT, retChan chan *big.Int, errChan chan error, quit chan bool) { one := big.NewInt(1) two := big.NewInt(2) var startBits int if c.Precomp == nil { _ = c.Precompute(2) } startBits = c.precompMaxBits // prepare values for the loop y := new(bn256.GT).Set(h) j := big.NewInt(0) // define first giant step giantStep := new(big.Int) giantStep.Exp(big.NewInt(2), big.NewInt(int64(startBits)), nil) z := new(bn256.GT).Neg(g) z.ScalarMult(z, giantStep) bound := new(big.Int).Exp(two, big.NewInt(2*int64(startBits)), nil) sh := sha1.New() for ; j.Cmp(bound) < 0; j.Add(j, giantStep) { select { case <-quit: return default: sh.Write([]byte(y.String())) e, ok := c.Precomp[string(sh.Sum(nil)[:10])] sh.Reset() if ok { retChan <- new(big.Int).Add(j, e) errChan <- nil return } y.Add(y, z) } } z.Add(z, z) x := new(bn256.GT).ScalarMult(g, new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(startBits)), nil)) T := make(map[string]*big.Int) for k,v := range c.Precomp { T[k] = v } bits := int64(c.m.BitLen()) for i := int64(startBits); i < bits; i++ { select { case <-quit: return default: // iteratively increasing giant step up to maximal value c.m giantStep.Exp(two, big.NewInt(i+1), nil) if giantStep.Cmp(c.m) > 0 { giantStep.Set(c.m) z.Neg(g) z.ScalarMult(z, c.m) } // for the selected giant step, add all the needed small steps for k := new(big.Int).Exp(two, big.NewInt(i), nil); k.Cmp(giantStep) < 0; k.Add(k, one) { select { case <-quit: return default: sh.Write([]byte(x.String())) T[string(sh.Sum(nil)[:10])] = new(big.Int).Set(k) sh.Reset() x = new(bn256.GT).Add(x, g) } } // make giant steps and search for the solution bound.Exp(giantStep, two, nil) for ; j.Cmp(bound) < 0; j.Add(j, giantStep) { select { case <-quit: return default: sh.Write([]byte(y.String())) e, ok := T[string(sh.Sum(nil)[:10])] sh.Reset() if ok { retChan <- new(big.Int).Add(j, e) errChan <- nil return } y.Add(y, z) } } z.Add(z, z) } } retChan <- nil errChan <- fmt.Errorf("failed to find the discrete logarithm within bound") } ================================================ FILE: internal/dlog/calc_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package dlog import ( "math/big" "testing" "github.com/fentec-project/bn256" "github.com/fentec-project/gofe/internal" "github.com/fentec-project/gofe/internal/keygen" "github.com/fentec-project/gofe/sample" "github.com/stretchr/testify/assert" ) func TestCalcZp_BabyStepGiantStep_ElGamal(t *testing.T) { modulusLength := 128 key, err := keygen.NewElGamal(modulusLength) if err != nil { t.Fatalf("Error in ElGamal key generation: %v", err) } bound := big.NewInt(100000000) // first test when x is positive sampler := sample.NewUniformRange(big.NewInt(2), bound) xCheck, err := sampler.Sample() if err != nil { t.Fatalf("Error during random int generation: %v", err) } h := new(big.Int).Exp(key.G, xCheck, key.P) calc, err := NewCalc().InZp(key.P, nil) if err != nil { t.Fatal("Error in creation of new CalcZp:", err) } calc = calc.WithBound(bound) x, err := calc.BabyStepGiantStep(h, key.G) if err != nil { t.Fatalf("Error in baby step - giant step algorithm: %v", err) } assert.Equal(t, xCheck, x, "BabyStepGiantStep result is wrong") // second test when the answer can also be negative sampler = sample.NewUniformRange(new(big.Int).Neg(bound), bound) xCheck, err = sampler.Sample() if err != nil { t.Fatalf("Error during random int generation: %v", err) } h = internal.ModExp(key.G, xCheck, key.P) calc = calc.WithNeg() x, err = calc.BabyStepGiantStep(h, key.G) if err != nil { t.Fatalf("Error in baby step - giant step algorithm: %v", err) } assert.Equal(t, xCheck.Cmp(x), 0, "BabyStepGiantStep result is wrong") } func TestCalcBN256_BabyStepGiantStep(t *testing.T) { bound := big.NewInt(1000000) sampler := sample.NewUniformRange(new(big.Int).Neg(bound), bound) xCheck, err := sampler.Sample() if err != nil { t.Fatalf("error when generating random number: %v", err) } //xCheck = big.NewInt(16) g := new(bn256.GT).ScalarBaseMult(big.NewInt(1)) h := new(bn256.GT) if xCheck.Sign() == 1 { h.ScalarMult(g, xCheck) } else { xCheckNeg := new(big.Int).Neg(xCheck) h.ScalarMult(g, xCheckNeg) h.Neg(h) } calc := NewCalc().InBN256().WithBound(bound).WithNeg() err = calc.Precompute(5) if err != nil { t.Fatalf("error when prcomputing: %v", err) } x, err := calc.BabyStepGiantStep(h, g) if err != nil { t.Fatalf("Error in baby step - giant step algorithm: %v", err) } assert.Equal(t, xCheck.Cmp(x), 0, "BabyStepGiantStep in BN256 returns wrong dlog") } ================================================ FILE: internal/dlog/dlog_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package dlog import ( "math/big" "testing" "github.com/fentec-project/gofe/internal/keygen" "github.com/fentec-project/gofe/sample" "github.com/stretchr/testify/assert" ) type params struct { p, order, g *big.Int } func getParams() (*params, error) { key, err := keygen.NewElGamal(20) if err != nil { return nil, err } return ¶ms{ p: key.P, order: key.Q, g: key.G, }, nil } func TestDLog(t *testing.T) { params, err := getParams() if err != nil { t.Fatalf("Error during parameters generation: %v", err) } sampler := sample.NewUniformRange(big.NewInt(2), params.order) xCheck, err := sampler.Sample() if err != nil { t.Fatalf("Error during random int generation: %v", err) } h := new(big.Int).Exp(params.g, xCheck, params.p) calc, _ := NewCalc().InZp(params.p, params.order) x1, err := calc.WithBound(nil).BabyStepGiantStep(h, params.g) if err != nil { t.Fatalf("Error in BabyStepGiantStep algorithm: %v", err) } x2, err := pollardRhoParallel(h, params.g, params.p, params.order) if err != nil { t.Fatalf("Error in Pollard rho algorithm: %v", err) } assert.Equal(t, xCheck.Cmp(x1), 0, "BabyStepGiantStep result is wrong") assert.Equal(t, xCheck.Cmp(x2), 0, "pollardRho result is wrong") } ================================================ FILE: internal/dlog/doc.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ // Package dlog includes algorithms for computing discrete logarithms. // // FE schemes instantiated from the Discrete Diffie-Hellman assumption // (DDH) all rely on efficient algorithms for calculating discrete // logarithms. package dlog ================================================ FILE: internal/dlog/pollard_rho.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package dlog import ( "fmt" "math" "math/big" "github.com/fentec-project/gofe/sample" ) // Pollard's rho algorithm - simple, non-parallel version. func pollardRho(h, g, p, order *big.Int) (*big.Int, error) { n := new(big.Int).Set(order) // a, b random from [1, n] sampler := sample.NewUniformRange(big.NewInt(1), new(big.Int).Add(n, big.NewInt(1))) a1, err := sampler.Sample() if err != nil { return nil, err } b1, err := sampler.Sample() if err != nil { return nil, err } // x = ((g ^ a mod p) * (h ^ b mod p ) mod p x1 := new(big.Int).Mod(new(big.Int).Mul(new(big.Int).Exp(g, a1, p), new(big.Int).Exp(h, b1, p)), p) x2, a2, b2 := new(big.Int).Set(x1), new(big.Int).Set(a1), new(big.Int).Set(b1) r, q, t := new(big.Int), new(big.Int), new(big.Int) iterate := func(x, a, b *big.Int) { s := r.Mod(x, big.NewInt(3)).Int64() switch s { case 0: x.Exp(x, big.NewInt(2), p) a.Mod(r.Mul(a, big.NewInt(2)), n) b.Mod(r.Mul(b, big.NewInt(2)), n) case 1: x.Mod(r.Mul(x, g), p) a.Mod(r.Add(a, big.NewInt(1)), n) case 2: x.Mod(r.Mul(x, h), p) b.Mod(r.Add(b, big.NewInt(1)), n) } } iterations := math.MaxInt32 one := big.NewInt(1) for i := 0; i < iterations; i++ { iterate(x1, a1, b1) iterate(x2, a2, b2) iterate(x2, a2, b2) if x1.Cmp(x2) == 0 { r.Mod(q.Sub(b2, b1), n) t.Mod(q.Sub(a1, a2), n) if r.Cmp(big.NewInt(0)) == 0 { return nil, fmt.Errorf("error in Pollard rho: no modular inverse for factor") } d := new(big.Int).GCD(nil, nil, r, n) // if r and n are coprime the algorithm is simple if d.Cmp(big.NewInt(1)) == 0 { q.ModInverse(r, n) return q.Mod(q.Mul(q, t), n), nil } // in case r and n are not coprime additional computations are needed r.Div(r, d) t.Div(t, d) nDivD := new(big.Int).Div(n, d) q.ModInverse(r, nDivD) q.Mod(q.Mul(q, t), nDivD) for j := big.NewInt(0); j.Cmp(d) == -1; j.Add(j, one) { if h.Cmp(new(big.Int).Exp(g, q, p)) == 0 { return q, nil } q.Add(q, nDivD) } } } return nil, fmt.Errorf("failed to find discrete logarithm in %d iterations", iterations) } type triple struct { x *big.Int a *big.Int b *big.Int } func process(x, a, b *big.Int, iterate func(x, a, b *big.Int), c chan<- triple, quit <-chan struct{}) { for { select { case <-quit: return default: // nonblocking } iterate(x, a, b) xab := triple{ x: new(big.Int).Set(x), a: new(big.Int).Set(a), b: new(big.Int).Set(b), } c <- xab } } // Parallelized Pollard's rho algorithm. func pollardRhoParallel(h, g, p, order *big.Int) (*big.Int, error) { n := new(big.Int).Set(order) iterate := func(x, a, b *big.Int) { r := new(big.Int) s := r.Mod(x, big.NewInt(3)).Int64() switch s { case 0: x.Exp(x, big.NewInt(2), p) a.Mod(r.Mul(a, big.NewInt(2)), n) b.Mod(r.Mul(b, big.NewInt(2)), n) case 1: x.Mod(r.Mul(x, g), p) a.Mod(r.Add(a, big.NewInt(1)), n) case 2: x.Mod(r.Mul(x, h), p) b.Mod(r.Add(b, big.NewInt(1)), n) } } c := make(chan triple, 8) quit := make(chan struct{}) triples := make(map[string]triple) r, q, t := new(big.Int), new(big.Int), new(big.Int) sampler := sample.NewUniformRange(big.NewInt(1), new(big.Int).Add(n, big.NewInt(1))) for i := 0; i < 8; i++ { a0, err := sampler.Sample() if err != nil { return nil, err } b0, err := sampler.Sample() if err != nil { return nil, err } x0 := new(big.Int).Mod(new(big.Int).Mul(new(big.Int).Exp(g, a0, p), new(big.Int).Exp(h, b0, p)), p) go process(x0, a0, b0, iterate, c, quit) } for { triple := <-c str := string(triple.x.Bytes()) if el, ok := triples[str]; ok { a1 := triple.a a2 := el.a b1 := triple.b b2 := el.b r.Mod(q.Sub(b2, b1), n) t.Mod(q.Sub(a1, a2), n) // presume that at least one goroutine will compute the right value, do not terminate with error here if r.Cmp(big.NewInt(0)) == 0 { triples[str] = triple continue } close(quit) d := new(big.Int).GCD(nil, nil, r, n) // if r and n are coprime the algorithm is simple if d.Cmp(big.NewInt(1)) == 0 { q.ModInverse(r, n) return q.Mod(q.Mul(q, t), n), nil } // in case r and n are not coprime additional computations are needed r.Div(r, d) t.Div(t, d) nDivD := new(big.Int).Div(n, d) q.ModInverse(r, nDivD) q.Mod(q.Mul(q, t), nDivD) one := big.NewInt(1) for j := big.NewInt(0); j.Cmp(d) == -1; j.Add(j, one) { if h.Cmp(new(big.Int).Exp(g, q, p)) == 0 { return q, nil } q.Add(q, nDivD) } } else { triples[str] = triple } } } const cycleSizeBound = 1024 * 1024 // first 1000 primes var smallPrimes = []int{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} // Returns the prime factorization of n. // The factorization is bounded by smoothness bound b (i.e. no factors can be bigger than b) func pollardRhoFactorization(n, b *big.Int) (map[string]int, error) { factors := make(map[string]int) // use the queue to process numbers until they are factorized to primes queue := make([]*big.Int, 0) queue = append(queue, n) var i *big.Int for len(queue) > 0 { i, queue = queue[0], queue[1:] // discard trivial factors if i.Cmp(big.NewInt(1)) == 0 { continue } // if the number is a prime, we save it if i.ProbablyPrime(20) { // we got a factor bigger than the smoothness bound - fail if b != nil && i.Cmp(b) > 0 { return nil, fmt.Errorf("bounded factorization failed: factor %d is bigger than bound %d", i, b) } stringified := string(i.Bytes()) if _, ok := factors[stringified]; ok { factors[stringified]++ } else { factors[stringified] = 1 } continue } // calculate a new divisor factor, err := pollardRhoCycle(i) if err != nil { return nil, err } // continue factoring the divisor and number/divisor queue = append(queue, factor) queue = append(queue, new(big.Int).Div(i, factor)) } if len(factors) == 0 { return nil, fmt.Errorf("failed to find the prime factors") } return factors, nil } // Returns a divisor of n. It runs the cycle of Pollard Rho until it encounters a non-trivial divisor. func pollardRhoCycle(n *big.Int) (*big.Int, error) { factor := big.NewInt(1) sampler := sample.NewUniform(n) // run until the returned value is > 1 for factor.Cmp(big.NewInt(1)) < 1 { // random starting values r, err := sampler.Sample() if err != nil { return nil, err } factor = runCycle(n, r) } return factor, nil } // Runs one cycle of the Pollard Rho algorithm. It may fail to find a divisor and may need to be restarted with // different starting values. func runCycle(n, x *big.Int) *big.Int { xFixed := new(big.Int).Set(x) cycleSize := 2 one := big.NewInt(1) factor := big.NewInt(1) for cycleSize < cycleSizeBound && factor.Cmp(one) == 0 { count := 0 for count < cycleSize && factor.Cmp(one) < 1 { // g(x) = (x^2 + 1) mod n x.Mod(new(big.Int).Add(new(big.Int).Mul(x, x), one), n) absDiff := new(big.Int).Abs(new(big.Int).Sub(x, xFixed)) factor.GCD(nil, nil, absDiff, n) count++ } cycleSize *= 2 xFixed.Set(x) } return factor } ================================================ FILE: internal/dlog/pollard_rho_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package dlog import ( "math/big" "math/rand" "testing" "time" "github.com/fentec-project/gofe/internal/keygen" "github.com/fentec-project/gofe/sample" "github.com/stretchr/testify/assert" ) func TestPollardRho(t *testing.T) { params, err := getParams() if err != nil { t.Fatalf("Error during parameters generation: %v", err) } sampler := sample.NewUniformRange(big.NewInt(2), params.order) xCheck, err := sampler.Sample() if err != nil { t.Fatalf("Error during random int generation: %v", err) } h := new(big.Int).Exp(params.g, xCheck, params.p) x, err := pollardRho(h, params.g, params.p, params.order) if err != nil { t.Fatalf("Error in Pollard rho algorithm: %v", err) } assert.Equal(t, xCheck.Cmp(x), 0, "pollardRho result is wrong") } func TestPollardRhoParallel(t *testing.T) { params, err := getParams() if err != nil { t.Fatalf("Error during parameters generation: %v", err) } sampler := sample.NewUniformRange(big.NewInt(2), params.order) xCheck, err := sampler.Sample() if err != nil { t.Fatalf("Error during random int generation: %v", err) } h := new(big.Int).Exp(params.g, xCheck, params.p) x, err := pollardRhoParallel(h, params.g, params.p, params.order) if err != nil { t.Fatalf("Error in Pollard rho algorithm: %v", err) } assert.Equal(t, xCheck.Cmp(x), 0, "pollardRho result is wrong") } func TestPollardRhoFactorization(t *testing.T) { sampler := sample.NewUniform(new(big.Int).Exp(big.NewInt(2), big.NewInt(32), nil)) n, err := sampler.Sample() if err != nil { t.Fatalf("Error during random int generation: %v", err) } factorization, err := pollardRhoFactorization(n, nil) if err != nil { t.Fatalf("error in pollard rho factorization: %v", err) } checkFactorization(factorization, n, t) } // Pollard rho with ElGamal group func TestPollardRhoElGamal(t *testing.T) { modulusLength := 32 key, err := keygen.NewElGamal(modulusLength) if err != nil { t.Fatalf("Error in group generation: %v", err) } order := key.Q sampler := sample.NewUniform(new(big.Int).Exp(big.NewInt(2), big.NewInt(30), nil)) xCheck, err := sampler.Sample() if err != nil { t.Fatalf("Error during random int generation: %v", err) } h := new(big.Int).Exp(key.G, xCheck, key.P) x, err := pollardRho(h, key.G, key.P, order) if err != nil { t.Fatalf("Error in Pollard rho algorithm: %v", err) } assert.Equal(t, xCheck.Cmp(x), 0, "pollardRho result is wrong") } func checkFactorization(factorization map[string]int, n *big.Int, t *testing.T) { nCheck := big.NewInt(1) for prime, power := range factorization { prime := new(big.Int).SetBytes([]byte(prime)) if !prime.ProbablyPrime(10) { t.Errorf("%d is not prime, but should be", prime) } nCheck.Mul(nCheck, new(big.Int).Exp(prime, big.NewInt(int64(power)), big.NewInt(0))) } assert.Equal(t, nCheck.Cmp(n), 0, "Product of factor powers should match original number") } // Hard factorization problem - the number is a product of two large primes func TestPollardRhoFactorizationHard(t *testing.T) { p := keygen.GetGermainPrime(35) q := keygen.GetGermainPrime(35) n := new(big.Int).Mul(p, q) factorization, err := pollardRhoFactorization(n, nil) if err != nil { t.Fatalf("error in pollard rho factorization: %v", err) } checkFactorization(factorization, n, t) } // Run Pollard rho factorization on a smooth number. func TestPollardRhoFactorizationBounded(t *testing.T) { B := smallPrimes[len(smallPrimes)-1] n := getSmoothNumber(B, 30, 10) factorization, err := pollardRhoFactorization(n, big.NewInt(int64(B))) if err != nil { t.Fatalf("error in bounded pollard rho factorization: %v", err) } checkFactorization(factorization, n, t) } func getSmoothNumber(B, numPrimes, maxPower int) *big.Int { // find the index of the maximal allowed factor indexBound := len(smallPrimes) for i, p := range smallPrimes { if p > B { indexBound = i break } } rand.Seed(time.Now().UTC().UnixNano()) prod := big.NewInt(1) // pick a random prime < B and a random power < 10, numPrimes times // multiply the cumulative product by prime ^ power for i := 0; i < numPrimes; i++ { prime := smallPrimes[rand.Intn(indexBound)] power := rand.Intn(maxPower + 1) prod.Mul(prod, new(big.Int).Exp(big.NewInt(int64(prime)), big.NewInt(int64(power)), big.NewInt(0))) } return prod } ================================================ FILE: internal/errors.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package internal import ( "fmt" ) var malformedStr = "is not of the proper form" // ErrMalformedPubKey is an error for public key. var ErrMalformedPubKey = fmt.Errorf("public key %s", malformedStr) // ErrMalformedSecKey is an error for secret key. var ErrMalformedSecKey = fmt.Errorf("secret key %s", malformedStr) // ErrMalformedDecKey is an error for derived key. var ErrMalformedDecKey = fmt.Errorf("decryption key %s", malformedStr) // ErrMalformedCipher is an error for ciphertext. var ErrMalformedCipher = fmt.Errorf("ciphertext %s", malformedStr) // ErrMalformedInput is an error for input data. var ErrMalformedInput = fmt.Errorf("input data %s", malformedStr) ================================================ FILE: internal/keygen/elgamal.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package keygen import ( "fmt" "math/big" "github.com/fentec-project/gofe/sample" ) // ElGamal holds paramenters for ElGamal scheme. type ElGamal struct { Y *big.Int // public key G *big.Int // generator P *big.Int // modulus Q *big.Int // (P - 1) / 2, i.e. order of G } // NewElGamal creates parameters for ElGamal scheme. Implementation is // adapted from https://github.com/dlitz/pycrypto/blob/master/lib/Crypto/PublicKey/ElGamal.py. func NewElGamal(modulusLength int) (*ElGamal, error) { p, err := GetSafePrime(modulusLength) if err != nil { return nil, fmt.Errorf("failed to generate safe prime") } zero := big.NewInt(0) one := big.NewInt(1) two := big.NewInt(2) three := big.NewInt(3) // q = (p - 1) / 2 q := new(big.Int).Sub(p, one) q.Div(q, two) var g *big.Int sampler := sample.NewUniformRange(three, p) for { g, err = sampler.Sample() if err != nil { return nil, err } // make g an element of the subgroup of quadratic residues g.Exp(g, two, p) // additional checks to avoid some known attacks if new(big.Int).Mod(new(big.Int).Sub(p, one), g).Cmp(zero) == 0 { continue } gInv := new(big.Int).ModInverse(g, p) if new(big.Int).Mod(new(big.Int).Sub(p, one), gInv).Cmp(zero) == 0 { continue } break } x, err := sampler.Sample() if err != nil { return nil, err } y := new(big.Int).Exp(g, x, p) return &ElGamal{ Y: y, G: g, P: p, Q: q, }, nil } ================================================ FILE: internal/keygen/prime.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package keygen import ( "crypto/rand" "fmt" "io" "math/big" ) // GetSafePrime returns a safe prime p (p = 2*p1 + 2 where p1 is prime too). func GetSafePrime(bits int) (p *big.Int, err error) { p1 := GetGermainPrime(bits - 1) p = big.NewInt(0) p.Mul(p1, big.NewInt(2)) p.Add(p, big.NewInt(1)) if p.BitLen() != bits { err := fmt.Errorf("bit length not correct") return nil, err } return p, nil } // GetGermainPrime returns a prime number p for which 2*p + 1 is also prime. Note that // conversely 2*p + 1 is called a safe prime. func GetGermainPrime(bits int) (p *big.Int) { // multiple germainPrime goroutines are called and we assume at least one will compute a // safe prime and send it to the channel, thus we do not handle errors in germainPrime c := make(chan *big.Int) quit := make(chan int) for j := int(0); j < 8; j++ { go germainPrime(bits, c, quit) } msg := <-c // for small values for parameter bits (which should be small only for testing) it sometimes // happen "send on closed channel" - so we leave the channel c to a garbage collector // close(c) close(quit) return msg } var smallPrimes = []uint8{ 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, } // smallPrimesProduct is the product of the values in smallPrimes and allows us // to reduce a candidate prime by this number and then determine whether it's // coprime to all the elements of smallPrimes without further big.Int // operations. var smallPrimesProduct = new(big.Int).SetUint64(16294579238595022365) // germainPrime is slightly modified Prime function from: // https://github.com/golang/go/blob/master/src/crypto/rand/util.go // germainPrime returns a number, p, of the given size, such that p and 2*p+1 are primes // with high probability. // germainPrime will return error for any error returned by rand.Read or if bits < 2. func germainPrime(bits int, c chan *big.Int, quit chan int) (p *big.Int, err error) { random := rand.Reader if bits < 2 { err = fmt.Errorf("crypto/rand: prime size must be at least 2-bit") return } b := uint(bits % 8) if b == 0 { b = 8 } bytes := make([]byte, (bits+7)/8) p = new(big.Int) p1 := new(big.Int) bigMod := new(big.Int) for { select { case <-quit: return default: // this is to make it non-blocking } _, err = io.ReadFull(random, bytes) if err != nil { return nil, err } // Clear bits in the first byte to make sure the candidate has a size <= bits. bytes[0] &= uint8(int(1<= 2 { bytes[0] |= 3 << (b - 2) } else { // Here b==1, because b cannot be zero. bytes[0] |= 1 if len(bytes) > 1 { bytes[1] |= 0x80 } } // Make the value odd since an even number this large certainly isn't prime. bytes[len(bytes)-1] |= 1 p.SetBytes(bytes) // Calculate the value mod the product of smallPrimes. If it's // a multiple of any of these primes we add two until it isn't. // The probability of overflowing is minimal and can be ignored // because we still perform Miller-Rabin tests on the result. bigMod.Mod(p, smallPrimesProduct) mod := bigMod.Uint64() NextDelta: for delta := uint64(0); delta < 1<<20; delta += 2 { m := mod + delta for _, prime := range smallPrimes { if m%uint64(prime) == 0 && (bits > 6 || m != uint64(prime)) { continue NextDelta } // 2*mod + 2*delta + 1 should not be divisible by smallPrimes as well m1 := (2*m + 1) % smallPrimesProduct.Uint64() if m1%uint64(prime) == 0 && (bits > 6 || m1 != uint64(prime)) { continue NextDelta } } if delta > 0 { bigMod.SetUint64(delta) p.Add(p, bigMod) } p1.Add(p, p) p1.Add(p1, big.NewInt(1)) break } // There is a tiny possibility that, by adding delta, we caused // the number to be one bit too long. Thus we check BitLen // here. if p.ProbablyPrime(20) && p.BitLen() == bits { if p1.ProbablyPrime(20) { // waiting for a message about channel being closed is repeated here, // because it might happen that channel is closed after waiting at the // beginning of for loop above (but we want to have it there also, // otherwise it this goroutine might be searching for a germain // prime for some time after one was found by another goroutine select { case <-quit: return default: // this is to make it non-blocking } c <- p return } } } } ================================================ FILE: internal/mod_exp.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package internal import "math/big" // ModExp calculates g^x in Z_m*, even if x < 0. func ModExp(g, x, m *big.Int) *big.Int { ret := new(big.Int) if x.Sign() == -1 { xNeg := new(big.Int).Neg(x) ret.Exp(g, xNeg, m) ret.ModInverse(ret, m) } else { ret.Exp(g, x, m) } return ret } ================================================ FILE: quadratic/doc.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ // Package quadratic includes functional encryption schemes for quadratic // multi-variate polynomials. package quadratic ================================================ FILE: quadratic/quad.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package quadratic import ( "fmt" "math/big" "github.com/fentec-project/bn256" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/innerprod/fullysec" "github.com/fentec-project/gofe/internal/dlog" "github.com/fentec-project/gofe/sample" ) // QuadParams includes public parameters for the partially // function hiding inner product scheme. // PartFHIPE: underlying partially function hiding scheme. // N (int): The length of x vectors to be encrypted. // M (int): The length of y vectors to be encrypted. // Bound (*big.Int): The value by which coordinates of vectors x, y and F are bounded. type QuadParams struct { PartFHIPE *fullysec.PartFHIPE // N should be greater or equal to M N int // length of vectors x M int // length of vectors y // The value by which elements of vectors x, y, and the // matrix F are bounded. Bound *big.Int } // Quad represents a public key FE scheme for quadratic multi-variate polynomials. // More precisely, it allows to encrypt vectors x and y using public key, // derive a functional encryption key corresponding to a matrix F, and // decrypt value x^T * F * y from encryption of x, y and functional key, without // reveling any other information about x or y. The scheme is based on // a paper by Romain Gay: "A New Paradigm for Public-Key Functional // Encryption for Degree-2 Polynomials". // The scheme uses an underling partially function hiding inner product // FE scheme. type Quad struct { Params *QuadParams } // NewQuad configures a new instance of the quadratic public key scheme. // It accepts the length of input vectors n and m and the upper bound b // for coordinates of input vectors x, y, and the function // matrix F. Parameter n should be greater or equal to m. func NewQuad(n, m int, b *big.Int) (*Quad, error) { if n < m { return nil, fmt.Errorf("n should be greater or equal to m") } bound := new(big.Int).Set(b) bound.Exp(bound, big.NewInt(3), nil) bound.Mul(big.NewInt(int64(2*n*m)), bound) if bound.Cmp(bn256.Order) > 0 { return nil, fmt.Errorf("bound and n, m too big for the group") } partFHIPE, err := fullysec.NewPartFHIPE(2*m+n*3, nil) if err != nil { return nil, err } return &Quad{ Params: &QuadParams{ PartFHIPE: partFHIPE, N: n, M: m, Bound: new(big.Int).Set(b), }, }, nil } // NewQuadFromParams takes configuration parameters of an existing // Quad instance, and reconstructs the scheme with the same configuration // parameters. It returns a new Quad instance. func NewQuadFromParams(params *QuadParams) *Quad { return &Quad{ Params: params, } } // QuadPubKey represents a public key for the scheme. // An instance of this type is returned by the // GenerateKeys method. type QuadPubKey struct { Ua data.VectorG1 VB data.MatrixG2 PubIPE *fullysec.PartFHIPEPubKey } // QuadSecKey represents a master secret key for the scheme. // An instance of this type is returned by the // GenerateKeys method. type QuadSecKey struct { U data.Matrix V data.Matrix SecIPE *fullysec.PartFHIPESecKey } // GenerateKeys generates a public key and master secret // key for the scheme. It returns an error if the keys could // not be generated. func (q *Quad) GenerateKeys() (*QuadPubKey, *QuadSecKey, error) { sampler := sample.NewUniform(bn256.Order) var err error // create a vector a over DDH distribution a := make(data.Vector, 2) a[0] = big.NewInt(1) a[1], err = sampler.Sample() if err != nil { return nil, nil, err } // create a vector a over DLIN distribution B := make(data.Matrix, 3) B[0] = make(data.Vector, 2) B[0][1] = big.NewInt(0) B[0][0], err = sampler.Sample() if err != nil { return nil, nil, err } B[1] = make(data.Vector, 2) B[1][0] = big.NewInt(0) B[1][1], err = sampler.Sample() if err != nil { return nil, nil, err } B[2] = data.NewConstantVector(2, big.NewInt(1)) // sample matrices U, V and calculate Ua, VB U, err := data.NewRandomMatrix(q.Params.N, 2, sampler) if err != nil { return nil, nil, err } V, err := data.NewRandomMatrix(q.Params.M, 3, sampler) if err != nil { return nil, nil, err } UaVec, err := U.MulVec(a) if err != nil { return nil, nil, err } UaVec = UaVec.Mod(bn256.Order) Ua := UaVec.MulG1() VBMat, err := V.Mul(B) if err != nil { return nil, nil, err } VBMat = VBMat.Mod(bn256.Order) VB := VBMat.MulG2() // assemble matrix M // upper part IdnVB, err := data.Identity(q.Params.M, q.Params.M).JoinCols(VBMat) if err != nil { return nil, nil, err } aMat := data.Matrix{a}.Transpose() aTensorIdnVB := aMat.Tensor(IdnVB) aTensorIdnVB = aTensorIdnVB.Mod(bn256.Order) M0, err := aTensorIdnVB.JoinCols(data.NewConstantMatrix(2*q.Params.M, q.Params.N*2, big.NewInt(0))) if err != nil { return nil, nil, err } // lower part IdnB := data.Identity(q.Params.N, q.Params.N).Tensor(B) M1, err := data.NewConstantMatrix(q.Params.N*3, IdnVB.Cols(), big.NewInt(0)).JoinCols(IdnB) if err != nil { return nil, nil, err } // together M, err := M0.JoinRows(M1) if err != nil { return nil, nil, err } pkIPE, skIPE, err := q.Params.PartFHIPE.GenerateKeys(M) if err != nil { return nil, nil, err } pk := &QuadPubKey{Ua: Ua, VB: VB, PubIPE: pkIPE} sk := &QuadSecKey{U: U, V: V, SecIPE: skIPE} return pk, sk, nil } // QuadCipher represents ciphertext in the scheme. type QuadCipher struct { Cx data.VectorG1 Cy data.VectorG2 CIPE data.VectorG1 } // Encrypt encrypts input vectors x and y with the given // public key. It returns the appropriate ciphertext. // If the ciphertext could not be generated, it returns an error. func (q *Quad) Encrypt(x, y data.Vector, pubKey *QuadPubKey) (*QuadCipher, error) { if len(x) != q.Params.N || len(y) != q.Params.M { return nil, fmt.Errorf("dimensions of vectors are incorrect") } if err := x.CheckBound(q.Params.Bound); err != nil { return nil, err } if err := y.CheckBound(q.Params.Bound); err != nil { return nil, err } sampler := sample.NewUniform(bn256.Order) r, err := sampler.Sample() if err != nil { return nil, err } s, err := data.NewRandomVector(2, sampler) if err != nil { return nil, err } Uar := pubKey.Ua.MulScalar(r) xG1 := x.MulG1() Cx := xG1.Add(Uar) VBs := pubKey.VB.MulVector(s) yG2 := y.MulG2() Cy := yG2.Add(VBs) ys := append(y, s...) rys := ys.MulScalar(r) xs := x.Tensor(s) xIPE := append(rys, xs...) xIPE = xIPE.Mod(bn256.Order) cIPE, err := q.Params.PartFHIPE.Encrypt(xIPE, pubKey.PubIPE) if err != nil { return nil, err } return &QuadCipher{Cx: Cx, Cy: Cy, CIPE: cIPE}, nil } // DeriveKey derives the functional encryption key for the scheme. // It returns an error if the key could not be derived. func (q *Quad) DeriveKey(secKey *QuadSecKey, F data.Matrix) (data.VectorG2, error) { if F.Rows() != q.Params.N || F.Cols() != q.Params.M { return nil, fmt.Errorf("dimensions of the given matrix are incorrect") } UtF, err := secKey.U.Transpose().Mul(F) if err != nil { return nil, err } UtF = UtF.Mod(bn256.Order) UTFvec := UtF.ToVec() FV, err := F.Mul(secKey.V) if err != nil { return nil, err } FV = FV.Mod(bn256.Order) FVvec := FV.ToVec() yIPE := append(UTFvec, FVvec...) feKey, err := q.Params.PartFHIPE.DeriveKey(yIPE, secKey.SecIPE) return feKey, err } // Decrypt decrypts the ciphertext c with the derived functional // encryption key key in order to obtain function x^T * F * y. func (q *Quad) Decrypt(c *QuadCipher, feKey data.VectorG2, F data.Matrix) (*big.Int, error) { if len(feKey) != q.Params.PartFHIPE.Params.L+4 { return nil, fmt.Errorf("dimensions of the given FE key are incorrect") } if F.Rows() != q.Params.N || F.Cols() != q.Params.M { return nil, fmt.Errorf("dimensions of the given matrix are incorrect") } d, err := q.Params.PartFHIPE.PartDecrypt(c.CIPE, feKey) if err != nil { return nil, err } FCy, err := F.MatMulVecG2(c.Cy) if err != nil { return nil, err } dec := new(bn256.GT).ScalarBaseMult(big.NewInt(0)) for i := 0; i < q.Params.N; i++ { pairedI := bn256.Pair(c.Cx[i], FCy[i]) dec = new(bn256.GT).Add(pairedI, dec) } d = new(bn256.GT).Neg(d) dec = new(bn256.GT).Add(dec, d) // get upper bounds b3 := new(big.Int).Exp(q.Params.Bound, big.NewInt(3), nil) b := new(big.Int).Mul(b3, big.NewInt(int64(q.Params.N*q.Params.M))) calc := dlog.NewCalc().InBN256().WithBound(b).WithNeg() res, err := calc.BabyStepGiantStep(dec, new(bn256.GT).ScalarBaseMult(big.NewInt(1))) return res, err } ================================================ FILE: quadratic/quad_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package quadratic_test import ( "math/big" "testing" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/quadratic" "github.com/fentec-project/gofe/sample" "github.com/stretchr/testify/assert" ) func TestQuad(t *testing.T) { // choose parameters for the encryption and build the scheme n := 10 m := 8 bound := big.NewInt(100) q, err := quadratic.NewQuad(n, m, bound) if err != nil { t.Fatalf("error when creating scheme: %v", err) } // generate public and secret key pubKey, secKey, err := q.GenerateKeys() if err != nil { t.Fatalf("error when generating keys: %v", err) } // sample vectors x and y that the encryptor will encrypt with public key; boundNeg := new(big.Int).Add(new(big.Int).Neg(bound), big.NewInt(1)) sampler := sample.NewUniformRange(boundNeg, bound) x, err := data.NewRandomVector(n, sampler) if err != nil { t.Fatalf("error when generating random vector: %v", err) } y, err := data.NewRandomVector(m, sampler) if err != nil { t.Fatalf("error when generating random vector: %v", err) } // simulate an encryptor that encrypts the two random vectors encryptor := quadratic.NewQuadFromParams(q.Params) c, err := encryptor.Encrypt(x, y, pubKey) if err != nil { t.Fatalf("error when encrypting: %v", err) } // derive a functional encryption key for a random matrix f f, err := data.NewRandomMatrix(n, m, sampler) if err != nil { t.Fatalf("error when generating random matrix: %v", err) } feKey, err := q.DeriveKey(secKey, f) if err != nil { t.Fatalf("error when deriving key: %v", err) } // simulate a decryptor that using FE key decrypt the x^T * f * y // without knowing x and y dec, err := q.Decrypt(c, feKey, f) if err != nil { t.Fatalf("error when decrypting: %v", err) } // check the correctness of the result check, err := f.MulXMatY(x, y) if err != nil { t.Fatalf("error when computing x*F*y: %v", err) } assert.Equal(t, check, dec, "Decryption wrong") } ================================================ FILE: quadratic/sgp.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package quadratic import ( "math/big" "github.com/fentec-project/bn256" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/internal/dlog" "github.com/fentec-project/gofe/sample" ) // SGP implements efficient FE scheme for quadratic multi-variate polynomials // based on Dufour Sans, Gay and Pointcheval: // "Reading in the Dark: Classifying Encrypted Digits with // Functional Encryption". // See paper: https://eprint.iacr.org/2018/206.pdf which is based on bilinear pairings. // It offers adaptive security under chosen-plaintext attacks (IND-CPA security). // This is a secret key scheme, meaning that we need a master secret key to // encrypt the messages. // Assuming input vectors x and y, the SGP scheme allows the decryptor to // calculate x^T * F * y, where F is matrix that represents the function, // and vectors x, y are only known to the encryptor, but not to decryptor. type SGP struct { // length of vectors x and y (matrix F is N x N) N int // Modulus for ciphertext and keys Mod *big.Int // The value by which elements of vectors x, y, and the // matrix F are bounded. Bound *big.Int GCalc *dlog.CalcBN256 GInvCalc *dlog.CalcBN256 } // NewSGP configures a new instance of the SGP scheme. // It accepts the length of input vectors n and the upper bound b // for coordinates of input vectors x, y, and the function // matrix F. func NewSGP(n int, b *big.Int) *SGP { return &SGP{ N: n, Mod: bn256.Order, Bound: b, GCalc: dlog.NewCalc().InBN256(), //.WithBound(b), GInvCalc: dlog.NewCalc().InBN256(), //.WithBound(b), } } // SGPSecKey represents a master secret key for the SGP scheme. // An instance of this type is returned by the // GenerateMasterKey method. type SGPSecKey struct { S data.Vector T data.Vector } // NewSGPSecKey constructs an instance of SGPSecKey. func NewSGPSecKey(s, t data.Vector) *SGPSecKey { return &SGPSecKey{ S: s, T: t, } } // GenerateMasterKey generates a master secret key for the // SGP scheme. It returns an error if the secret key could // not be generated. func (q *SGP) GenerateMasterKey() (*SGPSecKey, error) { // msk is random s, t from Z_p^n sampler := sample.NewUniform(q.Mod) s, err := data.NewRandomVector(q.N, sampler) if err != nil { return nil, err } t, err := data.NewRandomVector(q.N, sampler) if err != nil { return nil, err } return NewSGPSecKey(s, t), nil } // SGPCipher represents a ciphertext. An instance of this type // is returned as a result of the Encrypt method. type SGPCipher struct { G1MulGamma *bn256.G1 AMulG1 []data.VectorG1 BMulG2 []data.VectorG2 } // NewSGPCipher constructs an instance of SGPCipher. func NewSGPCipher(g1MulGamma *bn256.G1, aMulG1 []data.VectorG1, bMulG2 []data.VectorG2) *SGPCipher { return &SGPCipher{ G1MulGamma: g1MulGamma, AMulG1: aMulG1, BMulG2: bMulG2, } } // Encrypt encrypts input vectors x and y with the // master secret key msk. It returns the appropriate ciphertext. // If ciphertext could not be generated, it returns an error. func (q *SGP) Encrypt(x, y data.Vector, msk *SGPSecKey) (*SGPCipher, error) { sampler := sample.NewUniform(q.Mod) gamma, err := sampler.Sample() if err != nil { return nil, err } W, err := data.NewRandomMatrix(2, 2, sampler) if err != nil { return nil, err } WInv, err := W.InverseMod(q.Mod) if err != nil { return nil, err } WInvT := WInv.Transpose() a := make([]data.Vector, q.N) b := make([]data.Vector, q.N) for i := 0; i < q.N; i++ { // v = (x_i, gamma * s_i) tmp := new(big.Int).Mul(gamma, msk.S[i]) tmp.Mod(tmp, q.Mod) v := data.NewVector([]*big.Int{x[i], tmp}) ai, err := WInvT.MulVec(v) if err != nil { return nil, err } a[i] = ai.Mod(q.Mod) // v = (y_i, -t_i) tiNeg := new(big.Int).Sub(q.Mod, msk.T[i]) bi, err := W.MulVec(data.NewVector([]*big.Int{y[i], tiNeg})) if err != nil { return nil, err } b[i] = bi.Mod(q.Mod) } aMulG1 := make([]data.VectorG1, q.N) bMulG2 := make([]data.VectorG2, q.N) for i := range a { aMulG1[i] = a[i].MulG1() bMulG2[i] = b[i].MulG2() } c := NewSGPCipher(new(bn256.G1).ScalarBaseMult(gamma), aMulG1, bMulG2) return c, nil } // DeriveKey derives the functional encryption key for the scheme. // It returns an error if the key could not be derived. func (q *SGP) DeriveKey(msk *SGPSecKey, F data.Matrix) (*bn256.G2, error) { // F is matrix and represents function (x, y) -> Σ f_i,j * x_i * y_i. // Functional encryption key is g2 * f(s, t). v, err := F.MulXMatY(msk.S, msk.T) if err != nil { return nil, err } tmp := new(big.Int).Set(v) e := new(bn256.G2).ScalarBaseMult(big.NewInt(1)) if tmp.Cmp(big.NewInt(0)) < 0 { tmp.Neg(tmp) e.Neg(e) } return new(bn256.G2).ScalarMult(e, tmp), nil } // Decrypt decrypts the ciphertext c with the derived functional // encryption key key in order to obtain function x^T * F * y. func (q *SGP) Decrypt(c *SGPCipher, key *bn256.G2, F data.Matrix) (*big.Int, error) { prod := bn256.Pair(c.G1MulGamma, key) for i, row := range F { for j, rowEl := range row { if rowEl.Sign() != 0 { e1 := bn256.Pair(c.AMulG1[i][0], c.BMulG2[j][0]) e2 := bn256.Pair(c.AMulG1[i][1], c.BMulG2[j][1]) e := new(bn256.GT).Add(e1, e2) tmp := new(big.Int).Set(rowEl) if tmp.Sign() == -1 { tmp.Neg(tmp) e.Neg(e) } t := new(bn256.GT).ScalarMult(e, tmp) prod.Add(prod, t) } } } g1gen := new(bn256.G1).ScalarBaseMult(big.NewInt(1)) g2gen := new(bn256.G2).ScalarBaseMult(big.NewInt(1)) g := bn256.Pair(g1gen, g2gen) // b: b = n^2 * b^3 b3 := new(big.Int).Exp(q.Bound, big.NewInt(3), nil) n2 := new(big.Int).Exp(big.NewInt(int64(q.N)), big.NewInt(2), nil) b := new(big.Int).Mul(n2, b3) return q.GCalc.WithBound(b).WithNeg().BabyStepGiantStep(prod, g) } ================================================ FILE: quadratic/sgp_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package quadratic_test import ( "math/big" "testing" "github.com/fentec-project/gofe/data" "github.com/fentec-project/gofe/quadratic" "github.com/fentec-project/gofe/sample" "github.com/stretchr/testify/assert" ) func TestSGP(t *testing.T) { bound := big.NewInt(100) sampler := sample.NewUniformRange(new(big.Int).Neg(bound), bound) n := 10 f, err := data.NewRandomMatrix(n, n, sampler) if err != nil { t.Fatalf("error when generating random matrix: %v", err) } q := quadratic.NewSGP(n, bound) msk, err := q.GenerateMasterKey() if err != nil { t.Fatalf("error when generating master keys: %v", err) } x, err := data.NewRandomVector(n, sampler) if err != nil { t.Fatalf("error when generating random vector: %v", err) } y, err := data.NewRandomVector(n, sampler) if err != nil { t.Fatalf("error when generating random vector: %v", err) } //x[0].Set(big.NewInt(-10)) c, err := q.Encrypt(x, y, msk) if err != nil { t.Fatalf("error when encrypting: %v", err) } key, err := q.DeriveKey(msk, f) if err != nil { t.Fatalf("error when deriving key: %v", err) } check, err := f.MulXMatY(x, y) if err != nil { t.Fatalf("error when computing x*F*y: %v", err) } dec, err := q.Decrypt(c, key, f) if err != nil { t.Fatalf("error when decrypting: %v", err) } assert.Equal(t, check, dec, "Decryption wrong") } ================================================ FILE: sample/doc.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ // Package sample includes samplers for sampling random values // from different probability distributions. // // Package sample provides the Sampler interface // along with different implementations of this interface. // Its primary purpose is support choosing random *big.Int values // from selected probability distributions. // // Implementations of the Sampler interface can be used, // for instance, to fill vector or matrix structures with // the desired random data. package sample ================================================ FILE: sample/normal.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package sample import ( "crypto/rand" "encoding/binary" "math" "math/big" ) // Normal samples random values from the Normal (Gaussian) // probability distribution, centered on 0. type normal struct { // Standard deviation sigma *big.Float // Precision parameter n uint // Precomputed values of exponential function with precision n preExp []*big.Float // Precomputed values for quicker sampling powN *big.Int powNF *big.Float } // NewNormal returns an instance of Normal sampler. // It assumes mean = 0. func newNormal(sigma *big.Float, n uint) *normal { powN := new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(n)), nil) powNF := new(big.Float) powNF.SetPrec(n) powNF.SetInt(powN) return &normal{ sigma: sigma, n: n, preExp: nil, powN: powN, powNF: powNF, } } // expCoef are coefficients of a polynomial approximating an exponential // function. var expCoef = []float64{1.43291003789439094275872613876154915146798884961754e-7, 1.2303944375555413249736938854916878938183799618855e-6, 1.5359914219462011698283041005730353845137869939208e-5, 1.5396043210538638053991311593904356413986533880234e-4, 1.3333877552501097445841748978523355617653578519821e-3, 9.6181209331756452318717975913386908359825611114502e-3, 5.5504109841318247098307381293125217780470848083496e-2, 0.24022650687652774559310842050763312727212905883789, 0.69314718056193380668617010087473317980766296386719, 1} var mantissaPrecision = uint64(52) var mantissaMask = (uint64(1) << mantissaPrecision) - 1 var bitLenForSample = uint64(19) var maxExp = uint64(1023) var cmpMask = uint64(1) << 61 // Bernoulli returns true with probability proportional to // 2^{-t/l^2}. A polynomial approximation is used to evaluate // the exponential function. The implementation is based on paper: // "FACCT: FAst, Compact, and Constant-Time Discrete Gaussian // Sampler over Integers" by R. K. Zhao, R. Steinfeld, and A. Sakzad // (https://eprint.iacr.org/2018/1234.pdf). See the above paper where // it is argued that such a sampling achieves a relative error at most // 2^{-45} with the chosen parameters. func Bernoulli(t *big.Int, lSquareInv *big.Float) (bool, error) { aBig := new(big.Float).SetInt(t) aBig.Mul(aBig, lSquareInv) a, _ := aBig.Float64() a = -a negFloorA := -math.Floor(a) z := a + negFloorA powOfZ := expCoef[0] for i := 1; i < 10; i++ { powOfZ = powOfZ*z + expCoef[i] } powOfAMantissa := math.Float64bits(powOfZ) & mantissaMask powOfAExponent := (math.Float64bits(powOfZ) >> mantissaPrecision) - uint64(negFloorA) randBytes := make([]byte, 16) _, err := rand.Read(randBytes) if err != nil { return false, err } r1 := binary.LittleEndian.Uint64(randBytes[0:8]) r1 = r1 >> (64 - (mantissaPrecision + 1)) r2 := binary.LittleEndian.Uint64(randBytes[8:16]) r2 = r2 >> (64 - bitLenForSample) check1 := powOfAMantissa | (uint64(1) << mantissaPrecision) check2 := uint64(1) << (bitLenForSample + powOfAExponent + 1 - maxExp) // constant time check if r1 < check1 && r2 < check2 return (cmpMask&(r1-check1)&(r2-check2)) > 0 || powOfZ == 1, nil } // precompExp precomputes the values of exp(-2^i / 2 * sigma^2) needed // for sampling discrete Gauss distribution wit standard deviation sigma // to arbitrary precision. This is needed since such computations present // one of the bottlenecks of the computation. Values are precomputed in the // interval 0 <= i < sigma^2 * sqrt(n) since for greater i the results are // negligible. func (c normal) precompExp() []*big.Float { maxFloat := new(big.Float).Mul(c.sigma, big.NewFloat(math.Sqrt(float64(c.n)))) maxBits := maxFloat.MantExp(nil) * 2 vec := make([]*big.Float, maxBits+1) twoSigmaSquare := new(big.Float) twoSigmaSquare.SetPrec(c.n) twoSigmaSquare.Mul(c.sigma, c.sigma) twoSigmaSquare.Mul(twoSigmaSquare, big.NewFloat(2)) x := big.NewInt(1) for i := 0; i < maxBits+1; i++ { vec[i] = taylorExp(x, twoSigmaSquare, 8*c.n, c.n) x.Mul(x, big.NewInt(2)) } return vec } // isExpGreater outputs if y > exp(-x/(2*sigma^2)) with minimal // calculation of exp(-x/(2*sigma^2)) based on the precomputed // values. Sigma is implicit in the precomputed values saved in c. func (c normal) isExpGreater(y *big.Float, x *big.Int) bool { // set up an upper and lower bound for possible value of // exp(-x/(2*sigma^2)) upper := big.NewFloat(1) upper.SetPrec(c.n) lower := new(big.Float) lower.SetPrec(c.n) maxBits := x.BitLen() lower.Set(c.preExp[maxBits]) lower.Quo(lower, c.preExp[0]) if lower.Cmp(y) == 1 { return false } // based on bits of x and the precomputed values // change the upper and lower bound for i := 0; i < maxBits; i++ { bit := x.Bit(maxBits - 1 - i) if bit == 1 { upper.Mul(upper, c.preExp[maxBits-1-i]) if y.Cmp(upper) == 1 { return true } } else { lower.Quo(lower, c.preExp[maxBits-1-i]) if y.Cmp(lower) == -1 { return false } } } return false } // taylorExp approximates exp(-x/alpha) with taylor polynomial // of degree k, precise at least up to 2^-n. func taylorExp(x *big.Int, alpha *big.Float, k uint, n uint) *big.Float { // prepare the values for calculating the taylor polynomial of exp(x/sigma) res := big.NewFloat(1) res.SetPrec(n) val := new(big.Float) val.SetPrec(n) val.SetInt(x) val.Quo(val, alpha) powVal := new(big.Float) powVal.SetPrec(n) powVal.Set(val) factorial := new(big.Float) factorial.SetPrec(n) factorial.SetInt64(1) tmp := new(big.Float) tmp.SetPrec(n) // set up a minimal value up to which it calculates the precision oneOverEps := new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(n)), nil) eps := new(big.Float) eps.SetPrec(n) eps.SetInt(oneOverEps) eps.Quo(big.NewFloat(1), eps) // computation of the polynomial for i := uint(1); i <= k; i++ { tmp.Quo(powVal, factorial) res.Add(res, tmp) powVal.Mul(powVal, val) factorial.Mul(factorial, big.NewFloat(float64(i+1))) if tmp.Cmp(eps) == -1 { break } } res.Quo(big.NewFloat(1), res) return res } ================================================ FILE: sample/normal_cdt.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package sample import ( "crypto/rand" "encoding/binary" "math/big" ) // cdtTable consists of a precomputed table of values // using which one can create a constant time half-Gaussian // sampler with sigma = sqrt(1/2ln(2)) var cdtTable = [][2]uint64{{2200310400551559144, 3327841033070651387}, {7912151619254726620, 380075531178589176}, {5167367257772081627, 11604843442081400}, {5081592746475748971, 90134450315532}, {6522074513864805092, 175786317361}, {2579734681240182346, 85801740}, {8175784047440310133, 10472}, {2947787991558061753, 0}, {22489665999543, 0}} var cdtLen = 9 // upper bound on sample values var cdtLowMask uint64 = 0x7fffffffffffffff // SigmaCDT is a constant sqrt(1/(2ln(2))) var SigmaCDT, _ = new(big.Float).SetString("0.84932180028801904272150283410") // NormalCDT samples random values from the discrete Normal (Gaussian) // probability distribution, limited to non-negative values (half-Gaussian). // In particular each value x from Z^+ is sampled with probability proportional to // exp(-x^2/sigma^2) where sigma = sqrt(1/2ln(2)). // The implementation is based on paper: // "FACCT: FAst, Compact, and Constant-Time Discrete Gaussian // Sampler over Integers" by R. K. Zhao, R. Steinfeld, and A. Sakzad // (https://eprint.iacr.org/2018/1234.pdf). See the above paper where // it is argued that such a sampling achieves a relative error at most // 2^{-46} with the chosen parameters. type NormalCDT struct { *normal } // NewNormalCDT returns an instance of NormalCDT sampler. func NewNormalCDT() *NormalCDT { s := &NormalCDT{} return s } // Sample samples discrete non-negative values with Gaussian // distribution. func (c *NormalCDT) Sample() (*big.Int, error) { randBytes := make([]byte, 16) _, err := rand.Read(randBytes) if err != nil { return nil, err } r1 := binary.LittleEndian.Uint64(randBytes[0:8]) r1 = r1 & cdtLowMask r2 := binary.LittleEndian.Uint64(randBytes[8:16]) r2 = r2 & cdtLowMask x := uint64(0) for i := 0; i < cdtLen; i++ { x += (((r1 - cdtTable[i][0]) & ((uint64(1) << 63) ^ ((r2 - cdtTable[i][1]) | (cdtTable[i][1] - r2)))) | (r2 - cdtTable[i][1])) >> 63 } return big.NewInt(int64(x)), nil } ================================================ FILE: sample/normal_cdt_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package sample_test import ( "testing" "github.com/fentec-project/gofe/sample" ) func TestNormalCDT(t *testing.T) { var tests = []struct { name string expect paramBounds }{ { name: "CDT", expect: paramBounds{ meanLow: 0.39, meanHigh: 0.41, varLow: 0.48, varHigh: 0.5, }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { testNormalSampler( t, sample.NewNormalCDT(), test.expect, ) }) } } ================================================ FILE: sample/normal_cumulative.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package sample import ( "crypto/rand" "math" "math/big" "sort" ) // NormalCumulative samples random values from the // cumulative Normal (Gaussian) probability distribution, centered on 0. // This sampler is the fastest, but is limited only to cases when sigma // is not too big, due to the sizes of the precomputed tables. Note that // the sampler offers arbitrary precision but the implementation is not // constant time. type NormalCumulative struct { *normal // table of precomputed values relative to the cumulative distribution precomputed []*big.Int // twoSided defines if we limit sampling only to non-negative integers // or to all twoSided bool // integer defining from how big of an interval do we need to sample // uniformly to sample according to discrete Gauss sampleSize *big.Int } // NewNormalCumulative returns an instance of NormalCumulative sampler. // It assumes mean = 0. Values are precomputed when this function is // called, so that Sample merely returns a precomputed value. func NewNormalCumulative(sigma *big.Float, n uint, twoSided bool) *NormalCumulative { s := &NormalCumulative{ normal: newNormal(sigma, n), twoSided: twoSided, } s.precompute() s.sampleSize = new(big.Int).Set(s.precomputed[len(s.precomputed)-1]) if s.twoSided { s.sampleSize.Mul(s.sampleSize, big.NewInt(2)) } return s } // Sample samples discrete cumulative distribution with // precomputed values. func (c *NormalCumulative) Sample() (*big.Int, error) { u, err := rand.Int(rand.Reader, c.sampleSize) sample := new(big.Int).Set(u) sign := 1 // if we sample two sided, one bit is reserved for the sign of the output if c.twoSided && u.Cmp(c.precomputed[len(c.precomputed)-1]) != -1 { sample.Sub(sample, c.precomputed[len(c.precomputed)-1]) sign = -1 } // Find the precomputed sample i := sort.Search(len(c.precomputed), func(i int) bool { return sample.Cmp(c.precomputed[i]) != 1 }) res := big.NewInt(int64(sign) * int64(i-1)) return res, err } // precompCumu precomputes the values for sampling. // This can be used only if sigma is not too big. func (c *NormalCumulative) precompute() { cutF := new(big.Float).Mul(c.sigma, big.NewFloat(math.Sqrt(float64(c.n)))) cut, _ := cutF.Int64() cut = cut + 1 vec := make([]*big.Int, cut+1) // vec is a table of precomputed values vec[0] = big.NewInt(0) iSquare := new(big.Int) twoSigmaSquare := new(big.Float).Mul(c.sigma, c.sigma) twoSigmaSquare.Mul(twoSigmaSquare, big.NewFloat(2)) addF := new(big.Float) addF.SetPrec(c.n) add := new(big.Int) for i := int64(0); i < cut; i++ { iSquare.SetInt64(i * i) // compute the value of exp(-i^2/2sigma^2) with precision n. // Computing the taylor polynomial with 8 * n elements suffices value := taylorExp(iSquare, twoSigmaSquare, 8*c.n, c.n) // in the case of sampling all integers, sampling 0 is counted twice // as positive and as negative, hence its probability must be halved if i == 0 && c.twoSided { value.Quo(value, big.NewFloat(2)) } // calculate the relative probability addF.Mul(value, c.powNF) addF.Int(add) // save the relative probability vec[i+1] = new(big.Int).Add(vec[i], add) } c.precomputed = vec } ================================================ FILE: sample/normal_cumulative_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package sample_test import ( "math/big" "testing" "github.com/fentec-project/gofe/sample" ) func TestNormalCumulative(t *testing.T) { var tests = []struct { name string sigma *big.Float n uint twoSided bool expect paramBounds }{ { name: "TwoSided, sigma 10", sigma: big.NewFloat(10), n: 256, twoSided: true, expect: paramBounds{ meanLow: -2, meanHigh: 2, varLow: 90, varHigh: 110, }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { testNormalSampler( t, sample.NewNormalCumulative(test.sigma, test.n, test.twoSided), test.expect, ) }) } } ================================================ FILE: sample/normal_double.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package sample import ( "crypto/rand" "fmt" "math/big" ) // NormalDouble samples random values from the // normal (Gaussian) probability distribution, centered on 0. // This sampler works in a way that it first samples from a // NormalCumulative with some small sigma and then using // another sampling from uniform distribution creates a candidate // for the output, which is accepted or rejected with certain // probability. Note that the sampler offers arbitrary precision // but the implementation is not constant time. type NormalDouble struct { *normal // NormalCumulative sampler used in the first part samplerCumu *NormalCumulative // precomputed parameters used for sampling k *big.Int twiceK *big.Int } // NewNormalDouble returns an instance of NormalDouble sampler. // It assumes mean = 0. Values are precomputed when this function is // called, so that Sample merely samples a value. // sigma should be a multiple of firstSigma. Increasing firstSigma a bit speeds // up the algorithm but increases the number of precomputed values func NewNormalDouble(sigma *big.Float, n uint, firstSigma *big.Float) (*NormalDouble, error) { kF := new(big.Float) kF.Quo(sigma, firstSigma) if !kF.IsInt() { return nil, fmt.Errorf("Sigma should be a multiple of firstSigma") } k, _ := kF.Int(nil) twiceK := new(big.Int).Mul(k, big.NewInt(2)) s := &NormalDouble{ normal: newNormal(sigma, n), samplerCumu: NewNormalCumulative(firstSigma, n, false), k: k, twiceK: twiceK, } s.preExp = s.precompExp() return s, nil } // Sample samples according to discrete Gauss distribution using // NormalCumulative and second sampling. func (s *NormalDouble) Sample() (*big.Int, error) { // prepare values var sign int64 checkVal := new(big.Int) uF := new(big.Float) uF.SetPrec(s.n) for { sign = 1 // first sample according to discrete gauss with smaller // sigma x, err := s.samplerCumu.Sample() if err != nil { return nil, err } // sample uniformly from an interval y, err := rand.Int(rand.Reader, s.twiceK) if err != nil { return nil, err } // use the last sampling to decide the sign of the output if y.Cmp(s.k) != -1 { sign = -1 y.Sub(y, s.k) } // calculate the probability of the accepting the result checkVal.Mul(s.k, x) checkVal.Mul(checkVal, big.NewInt(2)) checkVal.Add(checkVal, y) checkVal.Mul(checkVal, y) // sample if accept the output u, err := rand.Int(rand.Reader, s.powN) if err != nil { return nil, err } // decide if accept uF.SetInt(u) uF.Quo(uF, s.powNF) if !s.isExpGreater(uF, checkVal) { // calculate the value that we accepted res := new(big.Int).Mul(s.k, x) res.Add(res, y) res.Mul(res, big.NewInt(sign)) // in case the value is 0 we need to sample again to // decide if we accept the value, otherwise we return // the value if res.Cmp(big.NewInt(0)) == 0 { bit, err := rand.Int(rand.Reader, big.NewInt(2)) if err != nil { return nil, err } if bit.Cmp(big.NewInt(0)) == 0 { return res, err } } else { return res, err } } } } ================================================ FILE: sample/normal_double_constant.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package sample import ( "crypto/rand" "math/big" ) // NormalDoubleConstant samples random values from the // normal (Gaussian) probability distribution, centered on 0. // This sampler works by double sampling: it first samples from a // fixed Gaussian distribution with NormalCDT and then using // another sampling from uniform distribution creates a candidate // for the output, which is accepted or rejected with certain // probability. The sampler algorithm is constant time in the // sense that the sampled value is independent of the time needed. // The implementation is based on paper: // "FACCT: FAst, Compact, and Constant-Time Discrete Gaussian Sampler // over Integers" by R. K. Zhao, R. Steinfeld, and A. Sakzad, // see https://eprint.iacr.org/2018/1234.pdf. // See the above paper for the argumentation of the choice of // parameters and proof of precision and security. type NormalDoubleConstant struct { *normal // NormalCDT sampler used in the first part samplerCDT *NormalCDT // sigma = l * sqrt(1/2ln(2)) l *big.Int // precomputed values for faster sampling lSquareInv *big.Float twiceL *big.Int } // NewNormalDoubleConstant returns an instance of NormalDoubleConstant // sampler. It assumes mean = 0. Parameter l needs to be given, such // that sigma = l * sqrt(1/2ln(2)). func NewNormalDoubleConstant(l *big.Int) *NormalDoubleConstant { lSquare := new(big.Float).SetInt(l) lSquare.Mul(lSquare, lSquare) lSquareInv := new(big.Float).Quo(big.NewFloat(1), lSquare) twiceL := new(big.Int).Mul(l, big.NewInt(2)) s := &NormalDoubleConstant{ normal: &normal{}, samplerCDT: NewNormalCDT(), l: new(big.Int).Set(l), lSquareInv: lSquareInv, twiceL: twiceL, } return s } // Sample samples according to discrete Gauss distribution using // NormalDoubleConstant and second sampling. func (s *NormalDoubleConstant) Sample() (*big.Int, error) { // prepare values var sign int64 var check bool checkVal := new(big.Int) res := new(big.Int) for { sign = 1 // first sample according to discrete gauss with smaller // sigma x, err := s.samplerCDT.Sample() if err != nil { return nil, err } // sample uniformly from an interval y, err := rand.Int(rand.Reader, s.twiceL) if err != nil { return nil, err } // use one bit of sampling to decide the sign of the output if y.Cmp(s.l) != -1 { sign = -1 y.Sub(y, s.l) } // partially calculate the result and the probability of accepting the result res.Mul(s.l, x) checkVal.Mul(res, big.NewInt(2)) checkVal.Add(checkVal, y) checkVal.Mul(checkVal, y) res.Add(res, y) // zeroCheck == 1 if and only if sign == 1 and res.Sign() == 0 zeroCheck := int64(res.Sign()) + sign // sample from Bernoulli to decide if accept check, err = Bernoulli(checkVal, s.lSquareInv) if err != nil { return nil, err } if check && zeroCheck != 1 { // calculate the final value that we accepted res.Mul(res, big.NewInt(sign)) return res, nil } } } ================================================ FILE: sample/normal_double_constant_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package sample_test import ( "math/big" "testing" "github.com/fentec-project/gofe/sample" ) func TestNormalDoubleConstant(t *testing.T) { sigmaCDTSquare := 0.84932180028801904272150283410 sigmaCDTSquare *= sigmaCDTSquare var tests = []struct { k *big.Int name string expect paramBounds }{ { name: "sigma= 1 * sqrt(1/(2*ln(2)))", k: big.NewInt(1), expect: paramBounds{ meanLow: -0.2, meanHigh: 0.2, varLow: sigmaCDTSquare - 0.02, varHigh: sigmaCDTSquare + 0.02, }, }, { name: "sigma= 10 * sqrt(1/(2*ln(2)))", k: big.NewInt(10), expect: paramBounds{ meanLow: -2, meanHigh: 2, varLow: 100 * (sigmaCDTSquare - 0.02), varHigh: 100 * (sigmaCDTSquare + 0.02), }, }, { name: "sigma= 1000 * sqrt(1/(2*ln(2)))", k: big.NewInt(1000), expect: paramBounds{ meanLow: -20, meanHigh: 20, varLow: 1000000 * (sigmaCDTSquare - 0.02), varHigh: 1000000 * (sigmaCDTSquare + 0.02), }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { sampler := sample.NewNormalDoubleConstant(test.k) testNormalSampler( t, sampler, test.expect, ) }) } } ================================================ FILE: sample/normal_double_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package sample_test import ( "math/big" "testing" "github.com/fentec-project/gofe/sample" "github.com/stretchr/testify/assert" ) func TestNewNormalDouble(t *testing.T) { var tests = []struct { name string sigma *big.Float sigmaFirst *big.Float n uint expect paramBounds }{ { name: "SigmaFirst=1, sigma=1.5", sigmaFirst: big.NewFloat(1), sigma: big.NewFloat(1.5), n: 256, expect: paramBounds{ meanLow: -0.5, meanHigh: 0.5, varLow: 90, varHigh: 110, }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { _, err := sample.NewNormalDouble(test.sigma, test.n, test.sigmaFirst) assert.Error(t, err) }) } } func TestNormalDouble(t *testing.T) { var tests = []struct { name string sigma *big.Float sigmaFirst *big.Float n uint expect paramBounds }{ { name: "SigmaFirst=1, sigma10", sigmaFirst: big.NewFloat(1), sigma: big.NewFloat(10), n: 256, expect: paramBounds{ meanLow: -2, meanHigh: 2, varLow: 90, varHigh: 110, }, }, { name: "SigmaFirst=1.5, sigma9", sigmaFirst: big.NewFloat(1.5), sigma: big.NewFloat(9), n: 256, expect: paramBounds{ meanLow: -2, meanHigh: 2, varLow: 70, varHigh: 100, }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { sampler, err := sample.NewNormalDouble(test.sigma, test.n, test.sigmaFirst) assert.NoError(t, err) testNormalSampler( t, sampler, test.expect, ) }) } } ================================================ FILE: sample/normal_negative.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package sample import ( "crypto/rand" "math" "math/big" "github.com/pkg/errors" ) // NormalNegative samples random values from the possible // outputs of Normal (Gaussian) probability distribution centered on 0 and // accepts or denies each sample with probability defined by the distribution type NormalNegative struct { *normal // cut defines from which interval we sample cut *big.Int // precomputed value so we do not need to calculate it each time twiceCutPlusOne *big.Int } // NewNormalNegative returns an instance of NormalNegative sampler. // It assumes mean = 0. Values are precomputed when this function is // called, so that Sample merely returns a precomputed value. func NewNormalNegative(sigma *big.Float, n uint) *NormalNegative { cutF := new(big.Float).Mul(sigma, big.NewFloat(math.Sqrt(float64(n)))) cut := new(big.Int) cut, _ = cutF.Int(cut) twiceCutPlusOne := new(big.Int).Mul(cut, big.NewInt(2)) twiceCutPlusOne = twiceCutPlusOne.Add(twiceCutPlusOne, big.NewInt(1)) s := &NormalNegative{ normal: newNormal(sigma, n), cut: cut, twiceCutPlusOne: twiceCutPlusOne, } s.preExp = s.precompExp() return s } // Sample samples a value from discrete Gaussian distribution based on // negative (rejection) sampling. func (c *NormalNegative) Sample() (*big.Int, error) { uF := new(big.Float) uF.SetPrec(c.n) nSquare := new(big.Int) for { // random sample from the interval n, err := rand.Int(rand.Reader, c.twiceCutPlusOne) if err != nil { return nil, errors.Wrap(err, "error while sampling") } n = n.Sub(n, c.cut) nSquare.Mul(n, n) // sample again to decide if we except the sampled value u, err := rand.Int(rand.Reader, c.powN) if err != nil { return nil, errors.Wrap(err, "error while sampling") } uF.SetInt(u) uF.Quo(uF, c.powNF) if !c.isExpGreater(uF, nSquare) { return n, nil } } } ================================================ FILE: sample/normal_negative_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package sample_test import ( "fmt" "math/big" "testing" "github.com/fentec-project/gofe/sample" ) func TestNormalNegative(t *testing.T) { var tests = []struct { sigma *big.Float n uint expect paramBounds }{ { sigma: big.NewFloat(10), n: 256, expect: paramBounds{ meanLow: -2, meanHigh: 2, varLow: 90, varHigh: 110, }, }, } for _, test := range tests { t.Run(fmt.Sprintf("Sigma=%v", test.sigma), func(t *testing.T) { testNormalSampler(t, sample.NewNormalNegative(test.sigma, test.n), test.expect) }) } } ================================================ FILE: sample/normal_test.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package sample_test import ( "testing" "math/big" "github.com/fentec-project/gofe/sample" "github.com/stretchr/testify/assert" ) func mean(vec []*big.Int) *big.Float { meanI := big.NewInt(0) for i := 0; i < len(vec); i++ { meanI.Add(meanI, vec[i]) } ret := new(big.Float).SetInt(meanI) ret.Quo(ret, big.NewFloat(float64(len(vec)))) return ret } func variance(vec []*big.Int) *big.Float { meanI := big.NewInt(0) square := new(big.Int) for i := 0; i < len(vec); i++ { square.Mul(vec[i], vec[i]) meanI.Add(meanI, square) } ret := new(big.Float).SetInt(meanI) ret.Quo(ret, big.NewFloat(float64(len(vec)))) return ret } // paramBounds holds the boundaries for acceptable mean // and variance values. type paramBounds struct { meanLow, meanHigh, varLow, varHigh float64 } // testNormalSampler takes a normal sampler and checks whether // it is producing expected results - such that mean and variance // of a series of samples are within acceptable bounds. func testNormalSampler(t *testing.T, s sample.Sampler, bounds paramBounds) { vec := make([]*big.Int, 100000) for i := 0; i < len(vec); i++ { vec[i], _ = s.Sample() } me, _ := mean(vec).Float64() v, _ := variance(vec).Float64() // me should be around 0 and v should be around 100 assert.True(t, me > bounds.meanLow, "mean value of the normal distribution is too small") assert.True(t, me < bounds.meanHigh, "mean value of the normal distribution is too big") assert.True(t, v > bounds.varLow, "variance of the normal distribution is too small") assert.True(t, v < bounds.varHigh, "variance of the normal distribution is too big") } ================================================ FILE: sample/sampler.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package sample import ( "math/big" ) // Sampler samples random big integer values. // Implementations of this interface provide a method for // sampling random values from the desired distribution. type Sampler interface { // Sample samples random big integer values, // possibly returning an error. Sample() (*big.Int, error) } ================================================ FILE: sample/uniform.go ================================================ /* * Copyright (c) 2018 XLAB d.o.o * * 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. */ package sample import ( "crypto/rand" "math/big" ) // UniformRange samples random values from the interval [min, max). type UniformRange struct { min *big.Int max *big.Int } // NewUniformRange returns an instance of the UniformRange sampler. // It accepts lower and upper bounds on the sampled values. func NewUniformRange(min, max *big.Int) *UniformRange { return &UniformRange{ min: min, max: max, } } // Sample samples random values from the interval [min, max). func (u *UniformRange) Sample() (*big.Int, error) { maxMinusMin := new(big.Int).Sub(u.max, u.min) res, err := rand.Int(rand.Reader, maxMinusMin) if err != nil { return nil, err } res.Add(res, u.min) return res, err } // Uniform samples random values from the interval [0, max). type Uniform struct { UniformRange } // NewUniform returns an instance of the Uniform sampler. // It accepts an upper bound on the sampled values. func NewUniform(max *big.Int) *UniformRange { return NewUniformRange(big.NewInt(0), max) } // Sample samples random values from the interval [0, max). func (u *Uniform) Sample() (*big.Int, error) { return rand.Int(rand.Reader, u.max) } // Bit samples a single random bit (value 0 or 1). type Bit struct { Uniform } // NewBit returns an instance of Bit sampler. func NewBit() *UniformRange { return NewUniform(big.NewInt(2)) }