module Paillier::ZKP

Public Class Methods

new(pubkey, message, valid_messages) click to toggle source

Wrapper function that creates a ZKP object for the user. Instead of needing to call Paillier::ZKP::ZKP.new(args), the user calls Paillier::ZKP.new(args).

Example:

>> myZKP = Paillier::ZKP.new(key, 65, [23, 38, 52, 65, 77, 94])
        => [#<@p = plaintext>, #<@pubkey = <key>>, #<@ciphertext = <ciphertext>>, #<@cyphertext = <ciphertext>>, #<@commitment = <commitment>>]

Arguments:

public_key: The key to be used for the encryption (Paillier::PublicKey)
plaintext: The message to be encrypted (Integer)
valid_messages: The set of valid messages for encryption (Array)

NOTE: the order of valid_messages should be the same for both prover and verifier

# File lib/paillier/zkp.rb, line 144
def self.new(pubkey, message, valid_messages)
        return Paillier::ZKP::ZKP.new(pubkey, message, valid_messages)
end
verifyZKP?(pubkey, ciphertext, valid_messages, commitment) click to toggle source

Function that verifies whether a ciphertext is within the set of valid messages.

Example:

>> Paillier::ZKP.verifyZKP?(key, ciphertext, [23, 38, 65, 77, 94], commitment)
=> true

Arguments:

pubkey: The key used for the encryption (Paillier::PublicKey)
ciphertext: The ciphertext generated using the public key (OpenSSL::BN)
valid_messages: The set of valid messages for encryption (Array)
commitment: The commitment generated by the prover (Paillier::ZKP::ZKPCommit)

NOTE: the order of valid_messages should be the same for both prover and verifier

# File lib/paillier/zkp.rb, line 162
def self.verifyZKP?(pubkey, ciphertext, valid_messages, commitment)
        u_s = Array.new
        for m_k in valid_messages do         
                # g_mk = g ^ m_k (mod n^2)
                g_mk = pubkey.g.to_bn.mod_exp(m_k.to_bn, pubkey.n_sq)
                # u_k = c / g_mk (mod n^2) = c * invmod(g_mk) (mod n^2)
                u_k = OpenSSL::BN.new(ciphertext).mod_mul( Paillier.modInv(g_mk, pubkey.n_sq), pubkey.n_sq )
                u_s.push(u_k)
        end

        # calculate the challenge_string
        sha256 = OpenSSL::Digest::SHA256.new
        for a_k in commitment.a_s do
                sha256 << a_k.to_s
        end
        challenge_string = sha256.digest.unpack('H*')[0].to_i(16)

        e_sum = 0.to_bn
        big_mod = 2.to_bn
        big_mod = big_mod ** 256
        for e_k in commitment.e_s do
                e_sum = (e_sum + e_k.to_bn) % big_mod
        end

        # first we check that the sum matches correctly
        unless e_sum == OpenSSL::BN.new(challenge_string)
                return false
        end
        # then we check that z_k^n = a_k * (u_k^e_k) (mod n^2)
        for i in (0 .. (commitment.z_s.size - 1)) do
                a_k = commitment.a_s[i]
                e_k = commitment.e_s[i]
                u_k = u_s[i]
                z_k = commitment.z_s[i]
                # left hand side
                # z_kn = z_k ^ n (mod n^2)
                z_kn = z_k.to_bn.mod_exp(pubkey.n, pubkey.n_sq)
                # right hand side
                # u_ke = u_k ^ e_k (mod n^2)
                u_ke = u_k.to_bn.mod_exp(e_k, pubkey.n_sq)
                # a_kue = a_k * u_ke (mod n^2)
                a_kue = a_k.to_bn.mod_mul(u_ke, pubkey.n_sq)

                # z_k ^ n ?= a_k * (u_k ^ e_k)
                unless(z_kn == a_kue)
                        return false
                end
        end
        # if it passes both tests, then we have validated the contents
        return true
end