The following methods are provided in the Paillier module
Paillier.generateKeypair(keysize) # Returns privateKey, publicKey Paillier.encrypt(publicKey, plaintext) Paillier.eAdd(publicKey, cyphertext1, cyphertext2) Paillier.eAddConst(publicKey, cyphertext, plaintext) Paillier.eMulConst(publicKey, cyphertext, plaintext) Paillier.decrypt(privateKey, publicKey, cyphertext) Paillier.sign(privateKey, publicKey, data) Paillier.validSignature?(publicKey, data, signature) Paillier::ZKP.new(publicKey, plaintext, validMessages) Paillier::ZKP.verifyZKP?(publicKey, ciphertext, validMessages, commitment)
Note that public and privaye keys, signatures, and ZKP commitments have
from_s methods to make them easy to serialize and save to a file / database or send over a network.
The ZKP object has
commitment methods to extract the information that must be sent to the server.
Examples of all of the above methods are provided below.
Encryption and decryption is trivial:
require 'paillier' privkey, pubkey = Paillier.generateKeypair(2048) x = 3 cx = Paillier.encrypt(pubkey, x) dx = Paillier.decrypt(privkey, pubkey, cx) # dx == 3
Adding two encrypted numbers together is also easy:
x = 3 y = 5 cx = Paillier.encrypt(pubkey, x) cy = Paillier.encrypt(pubkey, y) cz = Paillier.eAdd(pubkey, cx, cy) z = Paillier.decrypt(privkey, pubkey, cz) # z == 8
You can also add plaintext constants to a message:
x = 3 cx = Paillier.encrypt(pubkey, x) cy = Paillier.eAddConst(pubkey, cx, 2) y = Paillier.decrypt(privkey, pubkey, cy) # y == 5
Multiplying by a constant is just as easy:
x = 3 cx = Paillier.encrypt(pubkey, x) cy = Paillier.eMulConst(pubkey, cx, 2) y = Paillier.decrypt(privkey, pubkey, cy) # y == 6
Paillier provides detached signatures, which can validate that a message has not been tampered with, and has been signed by the specified keypair.
data = 1000 # Arbitrary example signature = Paillier.sign(privkey, pubkey, data) Paillier.validSignature?(pubkey, data, signature) # Will return true if signature okay
If there are a limited number of acceptable messages then we say a ciphertext is valid if its plaintext is in the set of acceptable messages. The ZKP allows a third party to prove a ciphertext is valid, without having the private key to read the message.
To prove a ciphertext is valid, the prover must create their ciphertext and a commitment, and send it to the validator. The validator can then call verifyZKP? to ensure the message is valid. The client and server must have agreed ahead of time on a set and order of valid messages.
Note: The ciphertext must be created with the ZKP object, as parts of the encryption process are re-used to create the commitment.
Client (prover) side:
msg = 7 valid_messages = [1, 3, 5, 7, 9] # Create a ZKP object which will generate the commitment and ciphertext used for the proof zkp = Paillier::ZKP.new(pubkey, msg, valid_messages) ciphertext = zkp.ciphertext commitment = zkp.commitment # The client now sends the ciphertext and commitment to the server
Server (verifier) side:
valid_messages = [1, 3, 5, 7, 9] message_okay = Paillier::ZKP.verifyZKP?(pubkey, ciphertext, valid_messages, commitment) if( message_okay ) # Plaintext is a valid message, but we don't know which one else # Plaintext is *not* a valid message end
It is often necessary to convert data into text to make it easy to send over a network, save into a database, and so on. Our Paillier library provides several methods to make this process easier.
Public key serialization:
privkey, pubkey = Paillier.generateKeypair(2048) stringKey = pubkey.to_s # stringKey can now be saved to a file, sent over network, etc newpubkey == Paillier::PublicKey.from_s(string)
Private key serialization:
privkey, pubkey = Paillier.generateKeypair(2048) stringKey = privkey.to_s # stringKey can now be saved to a file, sent to another program, etc newprivkey == Paillier::PrivateKey.from_s(string)
WARNING: Serialized private keys are in plaintext! This functionality is provided so keys can be exported to another program and saved securely. Do not send your serialized private key across a network or save it in an insecure environment.
sig = Paillier.sign(privkey, pubkey, data) stringSig = sig.to_s # stringSig can now be saved to a file, sent over network, etc newsig = Paillier::Signature.from_s(stringSig)
ZKP Commitment serialization:
zkp = Paillier::ZKP.new(pubkey, valid_messages, msg) commitment = zkp.commitment commitmentString = commitment.to_s # commitmentString can now be saved to a file, sent over network, etc newCommitment = Paillier::ZKP::ZKPCommit.from_s(commitment_string)