Program Listing for File base-multiparty.cpp
↰ Return to documentation for file (pke/lib/schemebase/base-multiparty.cpp)
//==================================================================================
// BSD 2-Clause License
//
// Copyright (c) 2014-2022, NJIT, Duality Technologies Inc. and other contributors
//
// All rights reserved.
//
// Author TPOC: contact@openfhe.org
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//==================================================================================
#include "schemebase/base-multiparty.h"
#include <cstdint>
#include <iostream>
#include <map>
#include <memory>
#include <set>
#include <utility>
#include <vector>
#include "cryptocontext.h"
#include "key/evalkey.h"
#include "key/evalkeyrelin.h"
#include "key/privatekey.h"
#include "key/publickey.h"
#include "schemebase/base-pke.h"
#include "schemebase/base-scheme.h"
#include "schemebase/rlwe-cryptoparameters.h"
namespace lbcrypto {
// makeSparse is not used by this scheme
template <class Element>
KeyPair<Element> MultipartyBase<Element>::MultipartyKeyGen(CryptoContext<Element> cc,
const std::vector<PrivateKey<Element>>& privateKeyVec,
bool makeSparse) {
const auto cryptoParams = std::dynamic_pointer_cast<CryptoParametersRLWE<Element>>(cc->GetCryptoParameters());
const auto elementParams = cryptoParams->GetElementParams();
// Private Key Generation
Element s(elementParams, Format::EVALUATION, true);
for (auto& pk : privateKeyVec)
s += pk->GetPrivateElement();
// Public Key Generation
DugType dug;
Element a(dug, elementParams, Format::EVALUATION);
Element e(cryptoParams->GetDiscreteGaussianGenerator(), elementParams, Format::EVALUATION);
NativeInteger ns = cryptoParams->GetNoiseScale();
// b = ns * e - a * s
if (ns != 1)
e *= ns;
Element b(std::move(e -= a * s));
KeyPair<Element> keyPair(std::make_shared<PublicKeyImpl<Element>>(cc),
std::make_shared<PrivateKeyImpl<Element>>(cc));
keyPair.secretKey->SetPrivateElement(std::move(s));
std::vector<Element> pkElems;
pkElems.reserve(2);
pkElems.push_back(std::move(b));
pkElems.push_back(std::move(a));
keyPair.publicKey->SetPublicElements(std::move(pkElems));
return keyPair;
}
template <class Element>
KeyPair<Element> MultipartyBase<Element>::MultipartyKeyGen(CryptoContext<Element> cc,
const PublicKey<Element> publicKey, bool makeSparse,
bool fresh) {
const auto cryptoParams = std::dynamic_pointer_cast<CryptoParametersRLWE<Element>>(cc->GetCryptoParameters());
const auto elementParams = cryptoParams->GetElementParams();
const auto paramsPK = cryptoParams->GetParamsPK();
if (!paramsPK)
OPENFHE_THROW("PrecomputeCRTTables() must be called before using precomputed params.");
const DggType& dgg = cryptoParams->GetDiscreteGaussianGenerator();
TugType tug;
Element s;
switch (cryptoParams->GetSecretKeyDist()) {
case GAUSSIAN:
s = Element(dgg, paramsPK, Format::EVALUATION);
break;
case UNIFORM_TERNARY:
s = Element(tug, paramsPK, Format::EVALUATION);
break;
case SPARSE_TERNARY:
case SPARSE_ENCAPSULATED:
s = Element(tug, paramsPK, Format::EVALUATION, 192);
break;
default:
OPENFHE_THROW("Unknown SecretKeyDist.");
}
const auto& pk = publicKey->GetPublicElements();
Element a(pk[1]);
Element e(dgg, paramsPK, Format::EVALUATION);
NativeInteger ns = cryptoParams->GetNoiseScale();
// b = ns * e - a * s
// When PRE is not used, a joint key is computed
if (ns != 1)
e *= ns;
Element b(std::move(e -= a * s));
if (!fresh)
b += pk[0];
auto sizeQ = elementParams->GetParams().size();
auto sizePK = paramsPK->GetParams().size();
if (sizePK > sizeQ)
s.DropLastElements(sizePK - sizeQ);
KeyPair<Element> keyPair(std::make_shared<PublicKeyImpl<Element>>(cc),
std::make_shared<PrivateKeyImpl<Element>>(cc));
keyPair.secretKey->SetPrivateElement(std::move(s));
std::vector<Element> pkElems;
pkElems.reserve(2);
pkElems.push_back(std::move(b));
pkElems.push_back(std::move(a));
keyPair.publicKey->SetPublicElements(std::move(pkElems));
return keyPair;
}
template <class Element>
EvalKey<Element> MultipartyBase<Element>::MultiKeySwitchGen(const PrivateKey<Element> oldPrivateKey,
const PrivateKey<Element> newPrivateKey,
const EvalKey<Element> evalKey) const {
return oldPrivateKey->GetCryptoContext()->GetScheme()->KeySwitchGen(oldPrivateKey, newPrivateKey, evalKey);
}
template <class Element>
std::shared_ptr<std::map<uint32_t, EvalKey<Element>>> MultipartyBase<Element>::MultiEvalAutomorphismKeyGen(
const PrivateKey<Element> privateKey, const std::shared_ptr<std::map<uint32_t, EvalKey<Element>>> evalKeyMap,
const std::vector<uint32_t>& indexList) const {
const Element& s = privateKey->GetPrivateElement();
uint32_t N = s.GetRingDimension();
if (indexList.size() > N - 1)
OPENFHE_THROW("size exceeds the ring dimension");
const auto cc = privateKey->GetCryptoContext();
// deduplicated work list; the base keys are looked up (and missing ones reported) before
// the parallel loop because an exception may not cross an OpenMP region boundary
const std::set<uint32_t> indexSet(indexList.begin(), indexList.end());
const std::vector<uint32_t> indices(indexSet.begin(), indexSet.end());
const uint32_t sz = indices.size();
std::vector<EvalKey<Element>> baseKeys(sz);
for (uint32_t i = 0; i < sz; ++i) {
auto evalKeyIterator = evalKeyMap->find(indices[i]);
if (evalKeyIterator == evalKeyMap->end()) {
OPENFHE_THROW("EvalKey for index [" + std::to_string(indices[i]) + "] is not found.");
}
baseKeys[i] = evalKeyIterator->second;
}
// pre-created map slots so the parallel loop assigns without a critical section
auto result = std::make_shared<std::map<uint32_t, EvalKey<Element>>>();
for (auto indx : indices)
(*result)[indx];
#pragma omp parallel for num_threads(OpenFHEParallelControls.GetThreadLimit(sz))
for (uint32_t i = 0; i < sz; ++i) {
uint32_t index = NativeInteger(indices[i]).ModInverse(2 * N).ConvertToInt();
std::vector<uint32_t> vec(N);
PrecomputeAutoMap(N, index, &vec);
auto privateKeyPermuted = std::make_shared<PrivateKeyImpl<Element>>(cc);
privateKeyPermuted->SetPrivateElement(s.AutomorphismTransform(index, vec));
(*result)[indices[i]] = MultiKeySwitchGen(privateKey, privateKeyPermuted, baseKeys[i]);
}
return result;
}
template <class Element>
std::shared_ptr<std::map<uint32_t, EvalKey<Element>>> MultipartyBase<Element>::MultiEvalAtIndexKeyGen(
const PrivateKey<Element> privateKey, const std::shared_ptr<std::map<uint32_t, EvalKey<Element>>> evalKeyMap,
const std::vector<int32_t>& indexList) const {
std::vector<uint32_t> autoIndices(indexList.size());
const auto cc = privateKey->GetCryptoContext();
const uint32_t M = privateKey->GetCryptoParameters()->GetElementParams()->GetCyclotomicOrder();
for (size_t i = 0; i < indexList.size(); i++)
autoIndices[i] = cc->GetScheme()->FindAutomorphismIndex(indexList[i], M);
return MultiEvalAutomorphismKeyGen(privateKey, evalKeyMap, autoIndices);
}
template <class Element>
std::shared_ptr<std::map<uint32_t, EvalKey<Element>>> MultipartyBase<Element>::MultiEvalSumKeyGen(
const PrivateKey<Element> privateKey,
const std::shared_ptr<std::map<uint32_t, EvalKey<Element>>> evalKeyMap) const {
const std::set<uint32_t> indexSet{AdvancedSHEBase<Element>::GenerateIndexListForEvalSum(privateKey)};
std::vector<uint32_t> indices(indexSet.begin(), indexSet.end());
return MultiEvalAutomorphismKeyGen(privateKey, evalKeyMap, indices);
}
template <class Element>
Ciphertext<Element> MultipartyBase<Element>::MultipartyDecryptLead(ConstCiphertext<Element> ciphertext,
const PrivateKey<Element> privateKey) const {
const auto cryptoParams =
std::dynamic_pointer_cast<CryptoParametersRLWE<Element>>(privateKey->GetCryptoParameters());
const std::shared_ptr<ParmType> elementParams = cryptoParams->GetElementParams();
const auto ns = cryptoParams->GetNoiseScale();
const std::vector<Element>& cv = ciphertext->GetElements();
const Element& s = privateKey->GetPrivateElement();
DggType dgg(NoiseFlooding::MP_SD);
Element e(dgg, elementParams, Format::EVALUATION);
if (ns != 1)
e *= typename Element::Integer(ns);
auto result = ciphertext->CloneEmpty();
result->SetElement(cv[0] + s * cv[1] + e);
return result;
}
template <class Element>
Ciphertext<Element> MultipartyBase<Element>::MultipartyDecryptMain(ConstCiphertext<Element> ciphertext,
const PrivateKey<Element> privateKey) const {
const auto cryptoParams =
std::dynamic_pointer_cast<CryptoParametersRLWE<Element>>(privateKey->GetCryptoParameters());
const std::shared_ptr<ParmType> elementParams = cryptoParams->GetElementParams();
const auto es = cryptoParams->GetNoiseScale();
const std::vector<Element>& cv = ciphertext->GetElements();
const Element& s = privateKey->GetPrivateElement();
DggType dgg(NoiseFlooding::MP_SD);
Element e(dgg, elementParams, Format::EVALUATION);
if (es != 1)
e *= typename Element::Integer(es);
auto result = ciphertext->CloneEmpty();
result->SetElement(s * cv[1] + e);
return result;
}
template <class Element>
DecryptResult MultipartyBase<Element>::MultipartyDecryptFusion(const std::vector<Ciphertext<Element>>& ciphertextVec,
NativePoly* plaintext) const {
Element b = (ciphertextVec.size() > 1) ? ciphertextVec[0]->GetElements()[0] + ciphertextVec[1]->GetElements()[0] :
ciphertextVec[0]->GetElements()[0];
for (size_t i = 2; i < ciphertextVec.size(); i++)
b += ciphertextVec[i]->GetElements()[0];
b.SetFormat(Format::COEFFICIENT);
*plaintext = b.ToNativePoly();
return DecryptResult(plaintext->GetLength());
}
template <class Element>
PublicKey<Element> MultipartyBase<Element>::MultiAddPubKeys(PublicKey<Element> publicKey1,
PublicKey<Element> publicKey2) const {
PublicKey<Element> publicKeySum = std::make_shared<PublicKeyImpl<Element>>(publicKey1->GetCryptoContext());
const Element& b1 = publicKey1->GetPublicElements()[0];
const Element& b2 = publicKey2->GetPublicElements()[0];
const Element& a = publicKey1->GetPublicElements()[1];
std::vector<Element> pkElems;
pkElems.reserve(2);
pkElems.push_back(b1 + b2);
pkElems.push_back(a);
publicKeySum->SetPublicElements(std::move(pkElems));
return publicKeySum;
}
template <class Element>
EvalKey<Element> MultipartyBase<Element>::MultiAddEvalKeys(EvalKey<Element> evalKey1, EvalKey<Element> evalKey2) const {
const auto cc = evalKey1->GetCryptoContext();
EvalKey<Element> evalKeySum = std::make_shared<EvalKeyRelinImpl<Element>>(cc);
const std::vector<Element>& a = evalKey1->GetAVector();
const std::vector<Element>& b1 = evalKey1->GetBVector();
const std::vector<Element>& b2 = evalKey2->GetBVector();
std::vector<Element> b;
b.reserve(a.size());
for (uint32_t i = 0; i < a.size(); i++) {
b.push_back(b1[i] + b2[i]);
}
evalKeySum->SetAVector(a);
evalKeySum->SetBVector(std::move(b));
return evalKeySum;
}
template <class Element>
EvalKey<Element> MultipartyBase<Element>::MultiAddEvalMultKeys(EvalKey<Element> evalKey1,
EvalKey<Element> evalKey2) const {
const auto cc = evalKey1->GetCryptoContext();
EvalKey<Element> evalKeySum = std::make_shared<EvalKeyRelinImpl<Element>>(cc);
const std::vector<Element>& a1 = evalKey1->GetAVector();
const std::vector<Element>& a2 = evalKey2->GetAVector();
const std::vector<Element>& b1 = evalKey1->GetBVector();
const std::vector<Element>& b2 = evalKey2->GetBVector();
std::vector<Element> a;
a.reserve(a1.size());
std::vector<Element> b;
b.reserve(a1.size());
for (uint32_t i = 0; i < a1.size(); i++) {
a.push_back(a1[i] + a2[i]);
b.push_back(b1[i] + b2[i]);
}
evalKeySum->SetAVector(std::move(a));
evalKeySum->SetBVector(std::move(b));
return evalKeySum;
}
template <class Element>
EvalKey<Element> MultipartyBase<Element>::MultiMultEvalKey(PrivateKey<Element> privateKey,
EvalKey<Element> evalKey) const {
const auto cc = evalKey->GetCryptoContext();
const auto cryptoParams = std::dynamic_pointer_cast<CryptoParametersRLWE<Element>>(cc->GetCryptoParameters());
const DggType& dgg = cryptoParams->GetDiscreteGaussianGenerator();
const auto elementParams = cryptoParams->GetElementParams();
EvalKey<Element> evalKeyResult = std::make_shared<EvalKeyRelinImpl<Element>>(cc);
const std::vector<Element>& a0 = evalKey->GetAVector();
const std::vector<Element>& b0 = evalKey->GetBVector();
const Element& s = privateKey->GetPrivateElement();
const auto ns = cryptoParams->GetNoiseScale();
std::vector<Element> a;
a.reserve(a0.size());
std::vector<Element> b;
b.reserve(a0.size());
for (uint32_t i = 0; i < a0.size(); i++) {
Element ea(dgg, elementParams, Format::EVALUATION);
if (ns != 1)
ea *= typename Element::Integer(ns);
a.push_back(std::move(ea += a0[i] * s));
Element eb(dgg, elementParams, Format::EVALUATION);
if (ns != 1)
eb *= typename Element::Integer(ns);
b.push_back(std::move(eb += b0[i] * s));
}
evalKeyResult->SetAVector(std::move(a));
evalKeyResult->SetBVector(std::move(b));
return evalKeyResult;
}
template <class Element>
std::shared_ptr<std::map<uint32_t, EvalKey<Element>>> MultipartyBase<Element>::MultiAddEvalAutomorphismKeys(
const std::shared_ptr<std::map<uint32_t, EvalKey<Element>>> evalKeyMap1,
const std::shared_ptr<std::map<uint32_t, EvalKey<Element>>> evalKeyMap2) const {
auto evalKeyMapAuto = std::make_shared<std::map<uint32_t, EvalKey<Element>>>();
for (auto it = evalKeyMap1->begin(); it != evalKeyMap1->end(); ++it) {
auto it2 = evalKeyMap2->find(it->first);
if (it2 != evalKeyMap2->end())
(*evalKeyMapAuto)[it->first] = MultiAddEvalKeys(it->second, it2->second);
}
return evalKeyMapAuto;
}
template <class Element>
std::shared_ptr<std::map<uint32_t, EvalKey<Element>>> MultipartyBase<Element>::MultiAddEvalSumKeys(
const std::shared_ptr<std::map<uint32_t, EvalKey<Element>>> evalKeyMap1,
const std::shared_ptr<std::map<uint32_t, EvalKey<Element>>> evalKeyMap2) const {
return MultiAddEvalAutomorphismKeys(evalKeyMap1, evalKeyMap2);
}
} // namespace lbcrypto
// the code below is from base-multiparty-impl.cpp
namespace lbcrypto {
template class MultipartyBase<DCRTPoly>;
} // namespace lbcrypto