Program Listing for File rns-pke.cpp

↰ Return to documentation for file (pke/lib/schemerns/rns-pke.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 "schemerns/rns-pke.h"

#include <cstdint>
#include <memory>
#include <utility>
#include <vector>

#include "ciphertext.h"
#include "key/privatekey.h"
#include "key/publickey.h"
#include "schemerns/rns-cryptoparameters.h"

namespace lbcrypto {

Ciphertext<DCRTPoly> PKERNS::Encrypt(DCRTPoly plaintext, const PrivateKey<DCRTPoly> privateKey) const {
    Ciphertext<DCRTPoly> ciphertext(std::make_shared<CiphertextImpl<DCRTPoly>>(privateKey));

    const std::shared_ptr<ParmType> ptxtParams = plaintext.GetParams();
    std::shared_ptr<std::vector<DCRTPoly>> ba = EncryptZeroCore(privateKey, ptxtParams);

    plaintext.SetFormat(EVALUATION);

    (*ba)[0] += plaintext;

    ciphertext->SetElements(std::move(*ba));
    ciphertext->SetNoiseScaleDeg(1);

    return ciphertext;
}

Ciphertext<DCRTPoly> PKERNS::Encrypt(DCRTPoly plaintext, const PublicKey<DCRTPoly> publicKey) const {
    Ciphertext<DCRTPoly> ciphertext(std::make_shared<CiphertextImpl<DCRTPoly>>(publicKey));

    const std::shared_ptr<ParmType> ptxtParams = plaintext.GetParams();
    std::shared_ptr<std::vector<DCRTPoly>> ba = EncryptZeroCore(publicKey, ptxtParams);

    plaintext.SetFormat(EVALUATION);

    (*ba)[0] += plaintext;

    ciphertext->SetElements(std::move(*ba));
    ciphertext->SetNoiseScaleDeg(1);

    return ciphertext;
}

DecryptResult PKERNS::Decrypt(ConstCiphertext<DCRTPoly> ciphertext, const PrivateKey<DCRTPoly> privateKey,
                              Poly* plaintext) const {
    const std::vector<DCRTPoly>& cv = ciphertext->GetElements();
    DCRTPoly b = DecryptCore(cv, privateKey);

    b.SetFormat(Format::COEFFICIENT);
    size_t sizeQl = b.GetParams()->GetParams().size();

    if (sizeQl == 0)
        OPENFHE_THROW("Decryption failure: No towers left; consider increasing the depth.");

    if (sizeQl == 1) {
        *plaintext = Poly(b.GetElementAtIndex(0), Format::COEFFICIENT);
    } else {
        *plaintext = b.CRTInterpolate();
    }

    return DecryptResult(plaintext->GetLength());
}

DecryptResult PKERNS::Decrypt(ConstCiphertext<DCRTPoly> ciphertext, const PrivateKey<DCRTPoly> privateKey,
                              NativePoly* plaintext) const {
    const std::vector<DCRTPoly>& cv = ciphertext->GetElements();
    DCRTPoly b = DecryptCore(cv, privateKey);

    b.SetFormat(Format::COEFFICIENT);
    const size_t sizeQl = b.GetParams()->GetParams().size();
    if (sizeQl != 1) {
        OPENFHE_THROW(
                "sizeQl " + std::to_string(sizeQl) +
                "!= 1. If sizeQl = 0, consider increasing the depth. If sizeQl > 1, check parameters (this is unsupported for NativePoly).");
    }

    *plaintext = b.GetElementAtIndex(0);

    return DecryptResult(plaintext->GetLength());
}

std::shared_ptr<std::vector<DCRTPoly>> PKERNS::EncryptZeroCore(const PrivateKey<DCRTPoly> privateKey,
                                                               const std::shared_ptr<ParmType> params) const {
    const auto cryptoParams = std::dynamic_pointer_cast<CryptoParametersRNS>(privateKey->GetCryptoParameters());

    const DCRTPoly& s = privateKey->GetPrivateElement();
    const auto ns = cryptoParams->GetNoiseScale();
    const DggType& dgg = cryptoParams->GetDiscreteGaussianGenerator();
    DugType dug;

    const std::shared_ptr<ParmType> elementParams = (params == nullptr) ? cryptoParams->GetElementParams() : params;

    DCRTPoly a(dug, elementParams, Format::EVALUATION);
    DCRTPoly e(dgg, elementParams, Format::EVALUATION);

    uint32_t sizeQ = s.GetParams()->GetParams().size();
    uint32_t sizeQl = elementParams->GetParams().size();

    DCRTPoly c0, c1;
    if (sizeQl != sizeQ) {
        // Clone only the towers of the secret key that are still needed.
        DCRTPoly scopy = s.CloneTowers(0, sizeQl - 1);

        c0 = a * scopy + ns * e;
        c1 = -a;
    } else {
        // Use secret key as is
        c0 = a * s + ns * e;
        c1 = -a;
    }

    auto result = std::make_shared<std::vector<DCRTPoly>>();
    result->reserve(2);
    result->push_back(std::move(c0));
    result->push_back(std::move(c1));
    return result;
}

std::shared_ptr<std::vector<DCRTPoly>> PKERNS::EncryptZeroCore(const PublicKey<DCRTPoly> publicKey,
                                                               const std::shared_ptr<ParmType> params) const {
    const auto cryptoParams = std::dynamic_pointer_cast<CryptoParametersRNS>(publicKey->GetCryptoParameters());

    const std::vector<DCRTPoly>& pk = publicKey->GetPublicElements();
    const auto ns = cryptoParams->GetNoiseScale();
    const DggType& dgg = cryptoParams->GetDiscreteGaussianGenerator();

    TugType tug;

    const std::shared_ptr<ParmType> elementParams = (params == nullptr) ? cryptoParams->GetElementParams() : params;
    // TODO (dsuponit): "tug" must be assigned with TernaryUniformGenerator. Otherwise the DCRTPoly constructor crashes.
    // check other files if "tug" is properly assigned
    // if (cryptoParams->GetSecretKeyDist() != GAUSSIAN) {
    //    OPENFHE_THROW("TugType tug must be assigned");
    //}
    DCRTPoly v = cryptoParams->GetSecretKeyDist() == GAUSSIAN ? DCRTPoly(dgg, elementParams, Format::EVALUATION) :
                                                                DCRTPoly(tug, elementParams, Format::EVALUATION);

    // noise generation with the discrete gaussian generator dgg
    DCRTPoly e0(dgg, elementParams, Format::EVALUATION);
    DCRTPoly e1(dgg, elementParams, Format::EVALUATION);

    uint32_t sizeQ = pk[0].GetParams()->GetParams().size();
    uint32_t sizeQl = elementParams->GetParams().size();

    DCRTPoly c0, c1;
    if (sizeQl != sizeQ) {
        // Clone only the towers of the public keys that are still needed.
        DCRTPoly p0 = pk[0].CloneTowers(0, sizeQl - 1);
        DCRTPoly p1 = pk[1].CloneTowers(0, sizeQl - 1);

        c0 = p0 * v + ns * e0;
        c1 = p1 * v + ns * e1;
    } else {
        // Use public keys as they are
        const DCRTPoly& p0 = pk[0];
        const DCRTPoly& p1 = pk[1];

        c0 = p0 * v + ns * e0;
        c1 = p1 * v + ns * e1;
    }

    auto result = std::make_shared<std::vector<DCRTPoly>>();
    result->reserve(2);
    result->push_back(std::move(c0));
    result->push_back(std::move(c1));
    return result;
}

DCRTPoly PKERNS::DecryptCore(const std::vector<DCRTPoly>& cv, const PrivateKey<DCRTPoly> privateKey) const {
    const DCRTPoly& s = privateKey->GetPrivateElement();

    size_t sizeQl = cv[0].GetParams()->GetParams().size();
    auto scopy = s.CloneTowers(0, sizeQl - 1);

    DCRTPoly sPower(scopy);

    DCRTPoly b(cv[0]);
    b.SetFormat(Format::EVALUATION);

    DCRTPoly ci;
    for (size_t i = 1; i < cv.size(); i++) {
        ci = cv[i];
        ci.SetFormat(Format::EVALUATION);

        b.MultAccEqNoCheck(sPower, ci);
        sPower *= scopy;
    }
    return b;
}

}  // namespace lbcrypto