0

I am using CLion IDE on macOS 12. My project name is test2. The contents of the project are: cmake-build-debug (directory), CMakeLists.txt, and main.cpp. I want to run main.cpp:

/* Copyright (C) 2019 IBM Corp.
 * This program is 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. See accompanying LICENSE file.
 */

// This is a sample program for education purposes only.
// It attempts to demonstrate the use of the API for the
// binary arithmetic operations that can be performed.

#include <iostream>

#include <helib/helib.h>
#include <helib/binaryArith.h>
#include <helib/intraSlot.h>

int main(int argc, char* argv[])
{
    /*  Example of binary arithmetic using the BGV scheme  */

    // First set up parameters.
    //
    // NOTE: The parameters used in this example code are for demonstration only.
    // They were chosen to provide the best performance of execution while
    // providing the context to demonstrate how to use the "Binary Arithmetic
    // APIs". The parameters do not provide the security level that might be
    // required by real use/application scenarios.

    // Plaintext prime modulus.
    long p = 2;
    // Cyclotomic polynomial - defines phi(m).
    long m = 4095;
    // Hensel lifting (default = 1).
    long r = 1;
    // Number of bits of the modulus chain.
    long bits = 500;
    // Number of columns of Key-Switching matrix (typically 2 or 3).
    long c = 2;
    // Factorisation of m required for bootstrapping.
    std::vector<long> mvec = {7, 5, 9, 13};
    // Generating set of Zm* group.
    std::vector<long> gens = {2341, 3277, 911};
    // Orders of the previous generators.
    std::vector<long> ords = {6, 4, 6};

    std::cout << "\n*********************************************************";
    std::cout << "\n*            Basic Binary Arithmetic Example            *";
    std::cout << "\n*            ===============================            *";
    std::cout << "\n*                                                       *";
    std::cout << "\n* This is a sample program for education purposes only. *";
    std::cout << "\n* It attempts to demonstrate the use of the API for the *";
    std::cout << "\n* binary arithmetic operations that can be performed.   *";
    std::cout << "\n*                                                       *";
    std::cout << "\n*********************************************************";
    std::cout << std::endl;

    std::cout << "Initialising context object..." << std::endl;
    // Initialize the context.
    // This object will hold information about the algebra created from the
    // previously set parameters.
    helib::Context context = helib::ContextBuilder<helib::BGV>()
            .m(m)
            .p(p)
            .r(r)
            .gens(gens)
            .ords(ords)
            .bits(bits)
            .c(c)
            .bootstrappable(true)
            .mvec(mvec)
            .build();

    // Print the context.
    context.printout();
    std::cout << std::endl;

    // Print the security level.
    std::cout << "Security: " << context.securityLevel() << std::endl;

    // Secret key management.
    std::cout << "Creating secret key..." << std::endl;
    // Create a secret key associated with the context.
    helib::SecKey secret_key(context);
    // Generate the secret key.
    secret_key.GenSecKey();

    // Generate bootstrapping data.
    secret_key.genRecryptData();

    // Public key management.
    // Set the secret key (upcast: SecKey is a subclass of PubKey).
    const helib::PubKey& public_key = secret_key;

    // Get the EncryptedArray of the context.
    const helib::EncryptedArray& ea = context.getEA();

    // Build the unpack slot encoding.
    std::vector<helib::zzX> unpackSlotEncoding;
    buildUnpackSlotEncoding(unpackSlotEncoding, ea);

    // Get the number of slot (phi(m)).
    long nslots = ea.size();
    std::cout << "Number of slots: " << nslots << std::endl;

    // Generate three random binary numbers a, b, c.
    // Encrypt them under BGV.
    // Calculate a * b + c with HElib's binary arithmetic functions, then decrypt
    // the result.
    // Next, calculate a + b + c with HElib's binary arithmetic functions, then
    // decrypt the result.
    // Finally, calculate popcnt(a) with HElib's binary arithmetic functions,
    // then decrypt the result.  Note that popcnt, also known as hamming weight
    // or bit summation, returns the count of non-zero bits.

    // Each bit of the binary number is encoded into a single ciphertext. Thus
    // for a 16 bit binary number, we will represent this as an array of 16
    // unique ciphertexts.
    // i.e. b0 = [0] [0] [0] ... [0] [0] [0]        ciphertext for bit 0
    //      b1 = [1] [1] [1] ... [1] [1] [1]        ciphertext for bit 1
    //      b2 = [1] [1] [1] ... [1] [1] [1]        ciphertext for bit 2
    // These 3 ciphertexts represent the 3-bit binary number 110b = 6

    // Note: several numbers can be encoded across the slots of each ciphertext
    // which would result in several parallel slot-wise operations.
    // For simplicity we place the same data into each slot of each ciphertext,
    // printing out only the back of each vector.
    // NB: fifteenOrLess4Four max is 15 bits. Later in the code we pop the MSB.
    long bitSize = 16;
    long outSize = 2 * bitSize;
    long a_data = NTL::RandomBits_long(bitSize);
    long b_data = NTL::RandomBits_long(bitSize);
    long c_data = NTL::RandomBits_long(bitSize);

    std::cout << "Pre-encryption data:" << std::endl;
    std::cout << "a = " << a_data << std::endl;
    std::cout << "b = " << b_data << std::endl;
    std::cout << "c = " << c_data << std::endl;

    // Use a scratch ciphertext to populate vectors.
    helib::Ctxt scratch(public_key);
    std::vector<helib::Ctxt> encrypted_a(bitSize, scratch);
    std::vector<helib::Ctxt> encrypted_b(bitSize, scratch);
    std::vector<helib::Ctxt> encrypted_c(bitSize, scratch);
    // Encrypt the data in binary representation.
    for (long i = 0; i < bitSize; ++i) {
        std::vector<long> a_vec(ea.size());
        std::vector<long> b_vec(ea.size());
        std::vector<long> c_vec(ea.size());
        // Extract the i'th bit of a,b,c.
        for (auto& slot : a_vec)
            slot = (a_data >> i) & 1;
        for (auto& slot : b_vec)
            slot = (b_data >> i) & 1;
        for (auto& slot : c_vec)
            slot = (c_data >> i) & 1;
        ea.encrypt(encrypted_a[i], public_key, a_vec);
        ea.encrypt(encrypted_b[i], public_key, b_vec);
        ea.encrypt(encrypted_c[i], public_key, c_vec);
    }

    // Although in general binary numbers are represented here as
    // std::vector<helib::Ctxt> the binaryArith APIs for HElib use the PtrVector
    // wrappers instead, e.g. helib::CtPtrs_vectorCt. These are nothing more than
    // thin wrapper classes to standardise access to different vector types, such
    // as NTL::Vec and std::vector. They do not take ownership of the underlying
    // object but merely provide access to it.
    //
    // helib::CtPtrMat_vectorCt is a wrapper for
    // std::vector<std::vector<helib::Ctxt>>, used for representing a list of
    // encrypted binary numbers.

    // Perform the multiplication first and put it in encrypted_product.
    std::vector<helib::Ctxt> encrypted_product;
    helib::CtPtrs_vectorCt product_wrapper(encrypted_product);
    helib::multTwoNumbers(
            product_wrapper,
            helib::CtPtrs_vectorCt(encrypted_a),
            helib::CtPtrs_vectorCt(encrypted_b),
            /*rhsTwosComplement=*/false, // This means the rhs is unsigned rather
            // than 2's complement.
            outSize, // Outsize is the limit on the number of bits in the output.
            &unpackSlotEncoding); // Information needed for bootstrapping.

    // Now perform the encrypted sum and put it in encrypted_result.
    std::vector<helib::Ctxt> encrypted_result;
    helib::CtPtrs_vectorCt result_wrapper(encrypted_result);
    helib::addTwoNumbers(
            result_wrapper,
            product_wrapper,
            helib::CtPtrs_vectorCt(encrypted_c),
            /*negative=*/false, // This means the number are unsigned rather than 2's
            // complement.
            &unpackSlotEncoding); // Information needed for bootstrapping.

    // Decrypt and print the result.
    std::vector<long> decrypted_result;
    helib::decryptBinaryNums(decrypted_result, result_wrapper, secret_key, ea);
    std::cout << "a*b+c = " << decrypted_result.back() << std::endl;

    // Now calculate the sum of a, b and c using the addManyNumbers function.
    encrypted_result.clear();
    decrypted_result.clear();
    std::vector<std::vector<helib::Ctxt>> summands = {encrypted_a,
                                                      encrypted_b,
                                                      encrypted_c};
    helib::CtPtrMat_vectorCt summands_wrapper(summands);
    helib::addManyNumbers(
            result_wrapper,
            summands_wrapper,
            0,                    // sizeLimit=0 means use as many bits as needed.
            &unpackSlotEncoding); // Information needed for bootstrapping.

    // Decrypt and print the result.
    helib::decryptBinaryNums(decrypted_result, result_wrapper, secret_key, ea);
    std::cout << "a+b+c = " << decrypted_result.back() << std::endl;

    // This section calculates popcnt(a) using the fifteenOrLess4Four
    // function.
    // Note: the output i.e. encrypted_result should be of size 4
    // because 4 bits are required to represent numbers in [0,15].
    encrypted_result.resize(4lu, scratch);
    decrypted_result.clear();
    encrypted_a.pop_back(); // drop the MSB since we only support up to 15 bits.
    helib::fifteenOrLess4Four(result_wrapper,
                              helib::CtPtrs_vectorCt(encrypted_a));

    // Decrypt and print the result.
    helib::decryptBinaryNums(decrypted_result, result_wrapper, secret_key, ea);
    std::cout << "popcnt(a) = " << decrypted_result.back() << std::endl;

    return 0;
}

This is my CMakeLists.txt:

cmake_minimum_required(VERSION 3.22)
project(test2)

set(CMAKE_CXX_STANDARD 23)

add_executable(test2 main.cpp)

include_directories(/opt/homebrew/include)

After pressing the run button, I get this message:

====================[ Build | test2 | Debug ]===================================
/Applications/CLion.app/Contents/bin/cmake/mac/bin/cmake --build /Users/jang/CLionProjects/test2/cmake-build-debug --target test2
[1/1] Linking CXX executable test2
FAILED: test2 
: && /Library/Developer/CommandLineTools/usr/bin/c++ -g -arch arm64 -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX12.3.sdk -mmacosx-version-min=12.0 -Wl,-search_paths_first -Wl,-headerpad_max_install_names  CMakeFiles/test2.dir/main.cpp.o -o test2   && :
Undefined symbols for architecture arm64:
  "_ntl_gfree(_ntl_gbigint_body*)", referenced from:
      NTL::ZZ::Deleter::apply(_ntl_gbigint_body*) in main.cpp.o
  "NTL::TerminalError(char const*)", referenced from:
      NTL::LogicError(char const*) in main.cpp.o
      NTL::ResourceError(char const*) in main.cpp.o
      NTL::MemoryError() in main.cpp.o
      NTL::DummyScopeGuard::~DummyScopeGuard() in main.cpp.o
  "NTL::RandomBits_long(long)", referenced from:
      _main in main.cpp.o
  "NTL::conv(double&, NTL::xdouble const&)", referenced from:
      NTL::to_double(NTL::xdouble const&) in main.cpp.o
  "helib::addTwoNumbers(helib::PtrVector<helib::Ctxt>&, helib::PtrVector<helib::Ctxt> const&, helib::PtrVector<helib::Ctxt> const&, long, std::__1::vector<NTL::Vec<long>, std::__1::allocator<NTL::Vec<long> > >*)", referenced from:
      _main in main.cpp.o
  "helib::addManyNumbers(helib::PtrVector<helib::Ctxt>&, helib::PtrMatrix<helib::Ctxt>&, long, std::__1::vector<NTL::Vec<long>, std::__1::allocator<NTL::Vec<long> > >*)", referenced from:
      _main in main.cpp.o
  "helib::multTwoNumbers(helib::PtrVector<helib::Ctxt>&, helib::PtrVector<helib::Ctxt> const&, helib::PtrVector<helib::Ctxt> const&, bool, long, std::__1::vector<NTL::Vec<long>, std::__1::allocator<NTL::Vec<long> > >*)", referenced from:
      _main in main.cpp.o
  "helib::decryptBinaryNums(std::__1::vector<long, std::__1::allocator<long> >&, helib::PtrVector<helib::Ctxt> const&, helib::SecKey const&, helib::EncryptedArray const&, bool, bool)", referenced from:
      _main in main.cpp.o
  "helib::fifteenOrLess4Four(helib::PtrVector<helib::Ctxt> const&, helib::PtrVector<helib::Ctxt> const&, long)", referenced from:
      _main in main.cpp.o
  "helib::lweEstimateSecurity(int, double, int)", referenced from:
      helib::Context::securityLevel() const in main.cpp.o
  "helib::buildUnpackSlotEncoding(std::__1::vector<NTL::Vec<long>, std::__1::allocator<NTL::Vec<long> > >&, helib::EncryptedArray const&)", referenced from:
      _main in main.cpp.o
  "helib::Ctxt::Ctxt(helib::PubKey const&, long)", referenced from:
      _main in main.cpp.o
  "helib::SecKey::genRecryptData()", referenced from:
      _main in main.cpp.o
  "helib::SecKey::GenSecKey(long, long)", referenced from:
      _main in main.cpp.o
  "helib::SecKey::SecKey(helib::Context const&)", referenced from:
      _main in main.cpp.o
  "helib::operator|(helib::IndexSet const&, helib::IndexSet const&)", referenced from:
      helib::Context::securityLevel() const in main.cpp.o
  "helib::ContextBuilder<helib::BGV>::build() const", referenced from:
      _main in main.cpp.o
  "helib::Context::printout(std::__1::basic_ostream<char, std::__1::char_traits<char> >&) const", referenced from:
      _main in main.cpp.o
  "helib::IndexSet::next(long) const", referenced from:
      helib::IndexSet::iterator::operator++() in main.cpp.o
  "vtable for helib::PubKey", referenced from:
      helib::PubKey::~PubKey() in main.cpp.o
  NOTE: a missing vtable usually means the first non-inline virtual member function has no definition.
  "vtable for helib::SecKey", referenced from:
      helib::SecKey::~SecKey() in main.cpp.o
  NOTE: a missing vtable usually means the first non-inline virtual member function has no definition.
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
ninja: build stopped: subcommand failed.

I think I should add something to my CMakeLists.txt, but what should I add?

zxcv
  • 153
  • 7
  • 2
    You're missing a `findpackage(helib REQUIRED)` to find the helib library and you're missing a `target_link_libraries(test2 helib)` to link the exe with the helib library. – Petesh Apr 26 '22 at 09:58

0 Answers0