Huelse / SEAL-Python

Microsoft SEAL 4.X For Python
MIT License
319 stars 66 forks source link

'seal.Ciphertext' object attribute 'scale' is read-only #118

Closed qub1tt closed 3 months ago

qub1tt commented 5 months ago

Hello, i use this library for a project use Homomorphic Encryption in RF based on this repo https://github.com/dhuynh95/cryptotree/

I tried to convert all the function from tenSEAL to your library standards because in tenSEAL API, the create_public_key() function don't work and i don't know why so I found this library and decided to use it since it's working fine. Until i encountered a problem in https://github.com/dhuynh95/cryptotree/blob/master/nbs/03_polynomials.ipynb

from typing import List, Union

def multiply_and_add_coeffs(powers: List[seal.Ciphertext], plain_coeffs: List[seal.Plaintext],
                            coeffs: List[float],
                            evaluator: seal.Evaluator,
                            scale: float,
                            tol=1e-6) -> Union[seal.Ciphertext]:
    assert len(powers) == len(plain_coeffs), f"Mismatch between length between powers {len(powers)} and coeffs {len(coeffs)}"

    """Multiplies the coefficients with the corresponding powers andd adds everything.

    If the polynomial is non-constant, returns the ciphertext of the polynomial evaluation.
    Else if the polynomials is constant, the plaintext of the constant term is returned.
    """
    output = seal.Ciphertext()
    a0 = plain_coeffs[0]
    a0_added = False

    for i in range(1, len(plain_coeffs)):
        # We first check if the coefficient is not too small otherwise we skip it
        coef = coeffs[i]
        if np.abs(coef) < tol:
            continue

        plain_coeff = plain_coeffs[i]
        power = powers[i]

        evaluator.mod_switch_to_inplace(plain_coeff, power.parms_id())

        temp = evaluator.multiply_plain(power, plain_coeff)
        evaluator.rescale_to_next_inplace(temp)

        if not a0_added:
            evaluator.mod_switch_to_inplace(a0, temp.parms_id())
            temp.scale = scale
            output = evaluator.add_plain(temp, a0)
            a0_added = True
        else:
            evaluator.mod_switch_to_inplace(output, temp.parms_id())
            # We rescale both to the same scale
            output.scale = scale
            temp.scale = scale
            evaluator.add_inplace(output, temp)
    if a0_added:
        return output
    else:
        return a0

And when i call the function:

def polyeval_tree(ctx : seal.Ciphertext, coeffs: List[float], 
                  evaluator: seal.Evaluator, encoder : seal.Encryptor,
                  relin_keys: seal.RelinKeys,
                  scale: float):

    degree = len(coeffs) - 1
    plain_coeffs = coeffs_to_plaintext(coeffs, encoder, scale)
    powers = compute_all_powers(ctx, degree, evaluator, relin_keys)
    output = multiply_and_add_coeffs(powers, plain_coeffs, coeffs, evaluator, scale)

    return output

coeffs = [1.0,0.0,1.0]

ptx = encoder.encode(2.0, scale)

print("Initial vector input : ") 
print_ptx(ptx)

ctx = encryptor.encrypt(ptx)

output = polyeval_tree(ctx, coeffs, evaluator, encoder, relin_keys, scale)

print("Polynomial considered : X^2 + 1")
print(f"Associated coeffs : {coeffs}")
print("Output of polynomials : ")
print_ctx(output)

I have this error:

Cell In[113], line 37 35 if not a0_added: 36 evaluator.mod_switch_to_inplace(a0, temp.parms_id()) ---> 37 temp.scale = scale 38 output = evaluator.add_plain(temp, a0) 39 a0_added = True AttributeError: 'seal.Ciphertext' object attribute 'scale' is read-only

Does it mean that the scale attribute cannot be change in a Ciphertext object? Appreciate if you can help me solve this problem because i intend to use this for my Cryptography project in uni. Thank you!

Huelse commented 5 months ago

There are some differences, you can read the scale by old_value = cipher.scale() and set the scale by cipher.scale(new_value). Check the examples may also help you.

qub1tt commented 5 months ago

Thanks for the reply! I got the code run perfectly. Sorry i want to ask one more thing, i've found there is a function name encrypt_symmetric() and apparently it's from the tenseal API library. image

This is from tenSEAL: image This is from your library: image

In this library there is no encrypt_symmetric() just normal encrypt(), so I want to know what's the difference between these 2 functions? and if i want to use the symmetric one in this library what function i have to call? Thanks again!

Huelse commented 5 months ago

The encrypt_symmetric methods were added at SEAL v3.4.0, it is used to generate the Ciphertext in secret-key mode, witch need an Encryptor init by the secret key, link. You can use it is this library now.

qub1tt commented 5 months ago

It said public key is not set, i know the init for p is not right i just want to test the function. It seems like it only encrypt with public key image

Huelse commented 5 months ago

I mean you could use encrypt_symmetric now, repull the code and build the lib.