microsoft / SEAL

Microsoft SEAL is an easy-to-use and powerful homomorphic encryption library.
https://www.microsoft.com/en-us/research/group/cryptography-research/
MIT License
3.61k stars 709 forks source link

Different results in Linux and macOS #51

Closed daimingY closed 5 years ago

daimingY commented 5 years ago

I implemented the same code in both Linux and macOS, but the results are different. Here's a small example to show the difference: Just push back the value -11668621335293.000000 785 times into a vector, encrypt then decrypt the value, and the results are different between Linux and macOS. The Linux version resulted with a much larger fluctuation.

macOS version: 10.13.6 Linux version: Ubuntu 16.04.6 LTS

Scheme is CKKS, poly_modulus_degree 32768.

Here's the code (the utils.h is used for print_parameters and print_vector as in \examples):

#include <vector>
#include "utils.h"
#include "seal/seal.h"

using namespace std;
using namespace seal;

int main(){
    EncryptionParameters parms(scheme_type::CKKS);
    size_t poly_modulus_degree = 32768;
    parms.set_poly_modulus_degree(poly_modulus_degree);
    parms.set_coeff_modulus(CoeffModulus::Create(
        poly_modulus_degree, { 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60 }));
    double scale = pow(2.0, 360);
    auto context = SEALContext::Create(parms);
    print_parameters(context);

    KeyGenerator keygen(context);
    auto public_key = keygen.public_key();
    auto secret_key = keygen.secret_key();
    Encryptor encryptor(context, public_key);
    Decryptor decryptor(context, secret_key);
    CKKSEncoder encoder(context);

    vector<double> input_vector;
    for(int i = 0; i < 785; i++){
        input_vector.push_back(-11668621335293.000000);
    }

    Plaintext input_plain;
    encoder.encode(input_vector, scale, input_plain);
    Ciphertext input_cipher;
    encryptor.encrypt(input_plain, input_cipher);
    decryptor.decrypt(input_cipher, input_plain);
    encoder.decode(input_plain, input_vector);
    print_vector(input_vector);
    return 0;
}

The results from macOS:

/
| Encryption parameters :
|   scheme: CKKS
|   poly_modulus_degree: 32768
|   coeff_modulus size: 840 (60 + 60 + 60 + 60 + 60 + 60 + 60 + 60 + 60 + 60 + 60 + 60 + 60 + 60) bits
\

    [ -11668621335293.000, -11668621335293.002, -11668621335293.000, -11668621335293.000, ..., 0.000, -0.000, 0.001, 0.001 ]

The results from Linux:

/
| Encryption parameters :
|   scheme: CKKS
|   poly_modulus_degree: 32768
|   coeff_modulus size: 840 (60 + 60 + 60 + 60 + 60 + 60 + 60 + 60 + 60 + 60 + 60 + 60 + 60 + 60) bits
\

    [ -11668621335268.215, -11668621335271.973, -11668621335271.979, -11668621335279.500, ..., -0.342, -0.342, -0.029, 0.033 ]

What could cause this difference?

WeiDaiWD commented 5 years ago

Thanks for reporting. This looks interesting. As I am getting access to a macOS machine, could you please provide your c++ compiler and its version on both systems (g++ --version)?

Also to be absolutely clear, have you changed any CMake configuration?

daimingY commented 5 years ago

I installed globally and didn't change CMake configuration. I installed g++ on Linux from sudo add-apt-repository ppa:ubuntu-toolchain-r/test and downloaded Xcode 10.1 for macOS.

Linux:

g++ (Ubuntu 8.3.0-16ubuntu3~16.04) 8.3.0

maxOS:

Apple LLVM version 10.0.0 (clang-1000.11.45.5)
Target: x86_64-apple-darwin17.7.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
kimlaine commented 5 years ago

Thanks for the interesting bug report! :) The problem was in native/src/seal/ckks.cpp in the constructor of CKKSEncoder. This used a complex called psi, that represented a complex root of unity. This was then raised to various powers to get other roots of unity. However, the implementation in glibc of pow didn't work very well in this case, and resulted in some significant inaccuracy in those powers. The problem was that the complex number had a magnitude of 1, but pow didn't take that into account. I changed to a polar coordinate representation of psi and constructed those complex numbers with std::polar instead after a simple multiplication of the angle (complex argument) with the desired power. This totally resolves the inaccuracies and the behavior is now similar in all platforms. We'll publish the update shortly.