CKKS ReKeyGen throws mismatched RingDimension for Keys of the same CryptoContext using the Python wrapper

Hello,

I want to do a Proxy ReEncryption using the CKKS scheme with the official OpenFHE python wrapper. However, whenever I try to call the ReKeyGen() method I either get a RingDimension mismatch followed by a SIGABRT or straight a SIGSEGV error.

My first approach was (1) serializing the cryptocontext, (2) deserializing it at another entity, (3) creating a new keypair, (4) sending the public key back and then (5) creating the ReKey. Given, that this didn’t work, i expected the serialization to be broken. However, even without serialization it doesn’t work. I tried using just one key pair and thus creating a ReKey for switching to the already existing secret key, but also having two key pairs and switching to the new key pair. Both didn’t work. Similarly different FHE parameters didn’t work. PRE was always enabled.

I furthermore tried rewriting the unittest to use CKKS instead of BFV and it didn’t work. BGV also didn’t work in this unit test and only BFV didn’t crash.

Implementing the same workflow with the openfhe-development library worked. So I think the problem might be in the python wrapper, but have too little knowledge to find a problem there.

The backtrace for the problem looks like so:

lbcrypto::PolyImpl<intnat::NativeVectorT<intnat::NativeIntegerT<unsigned long> > >::Times(lbcrypto::PolyImpl<intnat::NativeVectorT<intnat::NativeIntegerT<unsigned long> > > const&) const () from /my/path/.venv/lib/python3.10/site-packages/openfhe/lib/libOPENFHEpke.so.1
#13 0x00007ffff5f5d3c6 in lbcrypto::KeySwitchHYBRID::KeySwitchGenInternal(std::shared_ptr<lbcrypto::PrivateKeyImpl<lbcrypto::DCRTPolyImpl<bigintdyn::mubintvec<bigintdyn::ubint<unsigned long> > > > >, std::shared_ptr<lbcrypto::PublicKeyImpl<lbcrypto::DCRTPolyImpl<bigintdyn::mubintvec<bigintdyn::ubint<unsigned long> > > > >) const [clone ._omp_fn.0] ()
   from /my/path/.venv/lib/python3.10/site-packages/openfhe/lib/libOPENFHEpke.so.1
#14 0x00007ffff7b94977 in GOMP_parallel () from /lib/x86_64-linux-gnu/libgomp.so.1
#15 0x00007ffff5f5e278 in lbcrypto::KeySwitchHYBRID::KeySwitchGenInternal(std::shared_ptr<lbcrypto::PrivateKeyImpl<lbcrypto::DCRTPolyImpl<bigintdyn::mubintvec<bigintdyn::ubint<unsigned long> > > > >, std::shared_ptr<lbcrypto::PublicKeyImpl<lbcrypto::DCRTPolyImpl<bigintdyn::mubintvec<bigintdyn::ubint<unsigned long> > > > >) const ()
   from /my/path/.venv/lib/python3.10/site-packages/openfhe/lib/libOPENFHEpke.so.1
#16 0x00007ffff5f0b731 in lbcrypto::SchemeBase<lbcrypto::DCRTPolyImpl<bigintdyn::mubintvec<bigintdyn::ubint<unsigned long> > > >::KeySwitchGen(std::shared_ptr<lbcrypto::PrivateKeyImpl<lbcrypto::DCRTPolyImpl<bigintdyn::mubintvec<bigintdyn::ubint<unsigned long> > > > >, std::shared_ptr<lbcrypto::PublicKeyImpl<lbcrypto::DCRTPolyImpl<bigintdyn::mubintvec<bigintdyn::ubint<unsigned long> > > > >) const () from /my/path/.venv/lib/python3.10/site-packages/openfhe/lib/libOPENFHEpke.so.1
#17 0x00007ffff6031b7f in lbcrypto::PREBase<lbcrypto::DCRTPolyImpl<bigintdyn::mubintvec<bigintdyn::ubint<unsigned long> > > >::ReKeyGen(std::shared_ptr<lbcrypto::PrivateKeyImpl<lbcrypto::DCRTPolyImpl<bigintdyn::mubintvec<bigintdyn::ubint<unsigned long> > > > >, std::shared_ptr<lbcrypto::PublicKeyImpl<lbcrypto::DCRTPolyImpl<bigintdyn::mubintvec<bigintdyn::ubint<unsigned long> > > > >) const () from /my/path/.venv/lib/python3.10/site-packages/openfhe/lib/libOPENFHEpke.so.1
#18 0x00007ffff6038b3b in lbcrypto::SchemeBase<lbcrypto::DCRTPolyImpl<bigintdyn::mubintvec<bigintdyn::ubint<unsigned long> > > >::ReKeyGen(std::shared_ptr<lbcrypto::PrivateKeyImpl<lbcrypto::DCRTPolyImpl<bigintdyn::mubintvec<bigintdyn::ubint<unsigned long> > > > >, std::shared_ptr<lbcrypto::PublicKeyImpl<lbcrypto::DCRTPolyImpl<bigintdyn::mubintvec<bigintdyn::ubint<unsigned long> > > > >) const () from /my/path/.venv/lib/python3.10/site-packages/openfhe/lib/libOPENFHEpke.so.1

Please provide a minimal working example recreating the issue. OpenFHE-python comes with PRE examples that work without any exceptions, e.g., pre-buffer.py. The behavior has to be specific to the code used to instantiate PRE.

Of course. The working example pre-buffer.py also works for me, but when I adapt it to use the CKKS or BGV scheme, it throws an error saying a Modulus or RingDimension mismatch or a plain SIGSEGV occured.

A working example for CKKS could be:

    batch_size = 512
    parameters = CCParamsCKKSRNS()
    parameters.SetMultiplicativeDepth(2)
    parameters.SetScalingModSize(50)
    parameters.SetBatchSize(batch_size)

    cc:CryptoContext = GenCryptoContext(parameters)

    cc.Enable(PKESchemeFeature.PKE)
    cc.Enable(PKESchemeFeature.KEYSWITCH)
    cc.Enable(PKESchemeFeature.LEVELEDSHE)
    cc.Enable(PKESchemeFeature.ADVANCEDSHE)
    cc.Enable(PRE)

    keypair: KeyPair = cc.KeyGen()
    print(f"Alice keypair is good?: {keypair.good()}")

    keypair2: KeyPair = cc.KeyGen()
    print(f"Bob keypair is good?: {keypair.good()}")
        
    reKey = cc.ReKeyGen(keypair.secretKey, keypair2.publicKey)

and a working example for BGV could be:

    parameters = CCParamsBGVRNS()
    parameters.SetMultiplicativeDepth(2)
    parameters.SetPlaintextModulus(65537)

    cc: CryptoContext = GenCryptoContext(parameters)

    cc.Enable(PKESchemeFeature.PKE)
    cc.Enable(PKESchemeFeature.KEYSWITCH)
    cc.Enable(PKESchemeFeature.LEVELEDSHE)
    cc.Enable(PKESchemeFeature.ADVANCEDSHE)
    cc.Enable(PRE)

    keypair: KeyPair = cc.KeyGen()
    print(f"Alice keypair is good?: {keypair.good()}")

    keypair2: KeyPair = cc.KeyGen()
    print(f"Bob keypair is good?: {keypair.good()}")

    reKey = cc.ReKeyGen(keypair.secretKey, keypair2.publicKey)