예제 #1
0
def gprod_verify_inner(current_hash, crs, vec_crs_h_exp, C, inner_prod, inner_proof, len_gprod, n, logn):
    [crs_g, crs_h, u] = crs[:]

    ### Adding in zero knowledge
    [R, blinder_2,proof, last_b, last_c] = inner_proof[:]


    current_hash = hash_integers([current_hash,int(R[0]),int(R[1]), int(R[2]), blinder_2])
    x = current_hash % curve_order

    inner_prod = (inner_prod + blinder_2 * x ) % curve_order
    C = add(C, multiply(R,x))

    ### Putting inner_prod into exponent.
    current_hash = hash_integers([current_hash,inner_prod])
    x = current_hash % curve_order;

    u = multiply(u, x)
    C = add(C, multiply(u,inner_prod))

    vec_crs_g_exp = [1] * n;
    vec_crs_h_shifted = [1] * n;

    n_var = n
    for j in range(logn):
        n_var = n_var // 2
        [CL, CR] = proof[j]

        current_hash = hash_integers([current_hash,int(CL[0]),int(CL[1]), int(CL[2]),
                int(CR[0]), int(CR[1]), int(CR[2])])

        x = current_hash % curve_order; inv_x =inverse(x)

        for i in range(n):
            bin_i = int_to_binaryintarray(i,logn)
            if bin_i[logn - j - 1] == 1:
                vec_crs_g_exp[i] = (vec_crs_g_exp[i] * inv_x) % curve_order
                vec_crs_h_shifted[i] = (vec_crs_h_shifted[i] * x) % curve_order

        C = add(add(multiply(CR, inv_x), C), multiply(CL, x))

    vec_crs_h_exp[0] = (vec_crs_h_exp[0] * vec_crs_h_shifted[len_gprod - 1]) % curve_order
    for i in range(1,len_gprod):
        vec_crs_h_exp[i] = (vec_crs_h_exp[i] * vec_crs_h_shifted[i-1]) % curve_order
    for i in range(len_gprod,n):
        vec_crs_h_exp[i] = (vec_crs_h_exp[i] * vec_crs_h_shifted[i]) % curve_order

    inner_prod = last_b * last_c % curve_order
    final_g = compute_multiexp(crs_g[:], vec_crs_g_exp[:])
    final_h = compute_multiexp(crs_h[:], vec_crs_h_exp[:])

    expected_outcome = multiply(final_g, curve_order - last_b)
    expected_outcome = add(expected_outcome, multiply(final_h, curve_order - last_c))
    expected_outcome = add(expected_outcome, multiply(u, curve_order - inner_prod))

    if add( expected_outcome, C) != (1,1,0):
        print("ERROR: final exponent is incorrect")
        return [current_hash, 0]

    return [current_hash, 1]
예제 #2
0
def gprod_prove_inner(current_hash, crs, vec_b, vec_c,inner_prod, n, logn):
    [crs_g, crs_h, u] = crs[:]
    proof = []

    ### Adding in zero knowledge
    vec_r = [0]*n;
    for i in range(n):
        vec_r[i] = randbelow(curve_order)

    R = compute_multiexp(crs_h[:], vec_r[:])
    blinder_2 = compute_innerprod(vec_b[:], vec_r[:])

    current_hash = hash_integers([current_hash,int(R[0]),int(R[1]), int(R[2]), blinder_2])
    x = current_hash % curve_order

    inner_prod = (inner_prod + blinder_2 * x ) % curve_order
    for i in range(n):
        vec_c[i] = (vec_c[i] + vec_r[i] * x) % curve_order


    ### Inserting inner_prod into exponent.
    current_hash = hash_integers([current_hash,inner_prod])
    x = current_hash % curve_order;
    u = multiply(u, x)

    for j in range(logn):
        n = n // 2

        zL = compute_innerprod(vec_b[n:], vec_c[:n])
        zR = compute_innerprod(vec_b[:n], vec_c[n:])

        CL = add(compute_multiexp(crs_g[:n], vec_b[n:]), compute_multiexp(crs_h[n:],vec_c[:n]))
        CL = add(CL, multiply(u, zL))
        CR = add(compute_multiexp(crs_g[n:], vec_b[:n]), compute_multiexp(crs_h[:n],vec_c[n:]))
        CR = add(CR, multiply(u, zR))

        proof.append([CL, CR])

        current_hash = hash_integers([current_hash,int(CL[0]),int(CL[1]), int(CL[2]),
        int(CR[0]), int(CR[1]), int(CR[2])])

        x = current_hash % curve_order; inv_x =inverse(x);

        for i in range(n):
            crs_g[i] = add(multiply(crs_g[n + i],inv_x), crs_g[i] )
            crs_h[i] = add(multiply(crs_h[n + i],x), crs_h[i] )
            vec_b[i] =  (vec_b[i] + x * vec_b[n + i] ) % curve_order
            vec_c[i] =  (vec_c[i] + inv_x * vec_c[n + i] ) % curve_order

        crs_g = crs_g[:n]; crs_h = crs_h[:n]
        vec_b = vec_b[:n]; vec_c = vec_c[:n]

    return [current_hash, [R, blinder_2, proof[:], vec_b[0], vec_c[0]]]
예제 #3
0
def gprod_verify_outer(current_hash, crs, A, len_gprod, gprod, gprod_proof, n, logn):

    [crs_g, u, crs_se1, crs_se2] = crs[:]
    [B, blinder] = gprod_proof[:]

    current_hash = hash_integers([current_hash, gprod, blinder, int(B[0]), int(B[1]), int(B[2])])
    x = current_hash % curve_order; inv_x =inverse(x);

    C = crs_g[0]
    for i in range(1,len_gprod):
        C = add( C, crs_g[i])

    C = multiply(C, curve_order - inv_x)
    C = add(C, A)

    vec_crs_h_exp = [1]*n; pow_inv_x = inv_x
    for i in range(1,len_gprod):
        vec_crs_h_exp[i] = pow_inv_x
        pow_inv_x = pow_inv_x * inv_x % curve_order

    vec_crs_h_exp[0] = pow_inv_x

    pow_inv_x = pow_inv_x * inv_x % curve_order
    for i in range(len_gprod, n):
        vec_crs_h_exp[i] = pow_inv_x

    inner_prod = (blinder * (x ** (len_gprod+1)) + gprod * (x ** len_gprod) - 1) % curve_order

#    [current_hash, b] = gprod_verify_inner(current_hash, crs[:], vec_crs_h_exp[:], C, inner_prod, inner_proof[:],
#    len_gprod, n, logn)

    inner_prod_info = [vec_crs_h_exp[:], B, C, inner_prod, len_gprod]

    return [current_hash, inner_prod_info[:]]
예제 #4
0
def gprod_prove_outer(current_hash, crs, vec_a, len_gprod, gprod, n, logn):

    [crs_g, u, crs_se1, crs_se2] = crs[:]

    vec_b = [0] * n
    vec_b[0] = 1
    for i in range(1,len_gprod):
        vec_b[i] = vec_a[i] * vec_b[i-1] % curve_order

    for i in range(len_gprod,n):
        vec_b[i] = randbelow(curve_order)

    B = compute_multiexp(crs_g[:], vec_b[:])
    blinder = compute_innerprod(vec_a[len_gprod:],vec_b[len_gprod:])

    current_hash = hash_integers([current_hash, gprod, blinder, int(B[0]), int(B[1]), int(B[2])])
    x = current_hash % curve_order; inv_x =inverse(x);

    vec_c = [0] * n;
    pow_x = x;
    pow_x2 = 1
    for i in range(len_gprod-1):
        vec_c[i] = ( vec_a[i+1] * pow_x  - pow_x2) % curve_order
        pow_x = pow_x * x % curve_order
        pow_x2 = pow_x2 * x % curve_order

    vec_c[len_gprod-1] = (vec_a[0] * pow_x - pow_x2) % curve_order

    pow_x = pow_x * x % curve_order
    for i in range(len_gprod, n):
        vec_c[i] = (vec_a[i] * pow_x) % curve_order

    crs_h = [0]*n; pow_inv_x = inv_x
    for i in range(len_gprod - 1):
        crs_h[i] = multiply(crs_g[i+1], pow_inv_x)
        pow_inv_x = pow_inv_x * inv_x % curve_order
    crs_h[len_gprod-1] = multiply(crs_g[0], pow_inv_x)

    pow_inv_x = pow_inv_x * inv_x % curve_order
    for i in range(len_gprod, n):
        crs_h[i] = multiply(crs_g[i], pow_inv_x)

    inner_prod = (blinder * (x ** (len_gprod+1)) + gprod * (x ** len_gprod) - 1) % curve_order

    crs = [crs_g[:],crs_h[:],u]

#    [current_hash, inner_proof] = gprod_prove_inner(current_hash, crs[:], vec_b[:], vec_c[:], inner_prod, n, logn)

    inner_prod_info = [crs_h[:], vec_b[:], vec_c[:], inner_prod]

    return [current_hash, [B, blinder], inner_prod_info[:]]
def sameexp_verify(current_hash, g1, g2, h1, h2, y1, y2, sameexp_proof):

    [R1, R2, u1, u2, u3] = sameexp_proof[:]

    current_hash = hash_integers([
        current_hash,
        int(g1[0]),
        int(g1[1]),
        int(g1[2]),
        int(g2[0]),
        int(g2[1]),
        int(g2[2]),
        int(y1[0]),
        int(y1[1]),
        int(y1[2]),
        int(y2[0]),
        int(y2[1]),
        int(y2[2]),
        int(R1[0]),
        int(R1[1]),
        int(R1[2]),
        int(R2[0]),
        int(R2[1]),
        int(R2[2])
    ])

    x = current_hash % curve_order

    outcome1 = add(R1, multiply(y1, x))
    outcome1 = add(outcome1, multiply(g1, curve_order - u1))
    outcome1 = add(outcome1, multiply(h1, curve_order - u2))

    outcome2 = add(R2, multiply(y2, x))
    outcome2 = add(outcome2, multiply(g2, curve_order - u1))
    outcome2 = add(outcome2, multiply(h2, curve_order - u3))

    if outcome1 != Z1:
        print("ERROR: schnorr proof doesn't verify Z1")
        return (current_hash, 0)

    if outcome2 != Z1:
        print("ERROR: schnorr proof doesn't verify Z2")
        return (current_hash, 0)

    return (current_hash, 1)
def sameexp_prove(current_hash, g1, g2, h1, h2, y1, y2, exp, bl1, bl2):

    r = randbelow(curve_order)
    s = randbelow(curve_order)
    t = randbelow(curve_order)
    R1 = add(multiply(g1, r), multiply(h1, s))
    R2 = add(multiply(g2, r), multiply(h2, t))

    current_hash = hash_integers([
        current_hash,
        int(g1[0]),
        int(g1[1]),
        int(g1[2]),
        int(g2[0]),
        int(g2[1]),
        int(g2[2]),
        int(y1[0]),
        int(y1[1]),
        int(y1[2]),
        int(y2[0]),
        int(y2[1]),
        int(y2[2]),
        int(R1[0]),
        int(R1[1]),
        int(R1[2]),
        int(R2[0]),
        int(R2[1]),
        int(R2[2])
    ])

    x = current_hash % curve_order

    u1 = (r + x * exp) % curve_order
    u2 = (s + x * bl1) % curve_order
    u3 = (t + x * bl2) % curve_order

    return (current_hash, [R1, R2, u1, u2, u3])
예제 #7
0
def shuffle_prove(crs, num_blinders, ciphertexts_R, ciphertexts_S,
                  ciphertexts_T, ciphertexts_U, shuffle, r):

    [crs_g, u, crs_se1, crs_se2] = crs[:]
    n = len(ciphertexts_R) + num_blinders
    logn = int(math.log(n, 2))

    vec_m = [0] * n
    for i in range(len(ciphertexts_R)):
        vec_m[i] = shuffle[i]

    for i in range(len(ciphertexts_R), n):
        vec_m[i] = randbelow(curve_order)

    M = compute_multiexp(crs_g[:], vec_m[:])

    current_hash = hash_integers([int(M[0]), int(M[1]), int(M[2])])
    for i in range(len(ciphertexts_T)):
        current_hash = hash_integers([
            current_hash,
            int(ciphertexts_T[i][0]),
            int(ciphertexts_T[i][1]),
            int(ciphertexts_T[i][2]),
            int(ciphertexts_U[i][0]),
            int(ciphertexts_U[i][1]),
            int(ciphertexts_U[i][2])
        ])

    print("current_hash = ", current_hash)

    vec_a = [0] * len(ciphertexts_R)
    for i in range(len(ciphertexts_R)):
        vec_a[i] = current_hash % curve_order
        current_hash = hash_integers([current_hash])

    vec_a_shuffled = [0] * n
    for i in range(len(ciphertexts_R)):
        vec_a_shuffled[i] = vec_a[shuffle[i]]

    for i in range(len(ciphertexts_R), n):
        vec_a_shuffled[i] = randbelow(curve_order)

    A = compute_multiexp(crs_g[:], vec_a_shuffled[:])

    current_hash = hash_integers(
        [current_hash, int(A[0]),
         int(A[1]), int(A[2])])
    alpha = current_hash % curve_order

    current_hash = hash_integers([current_hash])
    beta = current_hash % curve_order

    vec_gprod = vec_a_shuffled[:]
    for i in range(n):
        vec_gprod[i] = (vec_gprod[i] + vec_m[i] * alpha + beta) % curve_order

    gprod = 1
    for i in range(len(ciphertexts_R)):
        gprod = (gprod * vec_gprod[i]) % curve_order

    start = timer()
    [current_hash, gprod_proof,
     inner_prod_info] = gprod_prove(current_hash, crs[:], vec_gprod[:],
                                    len(ciphertexts_R), gprod, n, logn)
    end = timer()
    print("gprod time = ", end - start)

    [crs_h, vec_b, vec_c, inner_prod] = inner_prod_info[:]

    start = timer()
    R = compute_multiexp(ciphertexts_R[:], vec_a[:])
    S = compute_multiexp(ciphertexts_S[:], vec_a[:])
    end = timer()
    print("R, S time = ", end - start, len(vec_a[:len(ciphertexts_R)]))

    vec_gammas = [0] * num_blinders
    vec_deltas = [0] * num_blinders
    current_hash = hash_integers(
        [current_hash, int(A[0]),
         int(A[1]), int(A[2])])
    for i in range(num_blinders):
        current_hash = hash_integers([current_hash])
        vec_gammas[i] = current_hash % curve_order
        current_hash = hash_integers([current_hash])
        vec_deltas[i] = current_hash % curve_order

    blinder_t = 0
    blinder_u = 0
    for i in range(num_blinders):
        blinder_t = (blinder_t + vec_gammas[i] *
                     vec_a_shuffled[len(ciphertexts_R) + i]) % curve_order
        blinder_u = (blinder_u + vec_deltas[i] *
                     vec_a_shuffled[len(ciphertexts_R) + i]) % curve_order

    T = add(multiply(R, r), multiply(crs_se1, blinder_t))
    U = add(multiply(S, r), multiply(crs_se2, blinder_u))

    for i in range(num_blinders):
        ciphertexts_T.append(multiply(crs_se1, vec_gammas[i]))
        ciphertexts_U.append(multiply(crs_se2, vec_deltas[i]))

    (current_hash, sameexp_proof) = sameexp_prove(current_hash, R, S, crs_se1,
                                                  crs_se2, T, U, r, blinder_t,
                                                  blinder_u)

    start = timer()
    crs = [crs_g[:], crs_h[:], u]
    [current_hash, gprod_and_multiexp_proof] = prove_multiexp_and_gprod_inner(
        current_hash, crs[:], vec_b[:], vec_c[:], inner_prod, ciphertexts_T[:],
        ciphertexts_U[:], vec_a_shuffled[:], n, logn)
    end = timer()
    print("inner product time = ", end - start)

    return [
        M, A, gprod_proof[:], T, U, sameexp_proof, gprod_and_multiexp_proof
    ]
예제 #8
0
def shuffle_verify(crs, num_blinders, ciphertexts_R, ciphertexts_S,
                   ciphertexts_T, ciphertexts_U, shuffle_proof):

    [crs_g, u, crs_se1, crs_se2] = crs[:]
    n = len(ciphertexts_R) + num_blinders
    logn = int(math.log(n, 2))

    [M, A, gprod_proof, T, U, sameexp_proof, inner_proof] = shuffle_proof[:]

    current_hash = hash_integers([int(M[0]), int(M[1]), int(M[2])])
    for i in range(len(ciphertexts_T)):
        current_hash = hash_integers([
            current_hash,
            int(ciphertexts_T[i][0]),
            int(ciphertexts_T[i][1]),
            int(ciphertexts_T[i][2]),
            int(ciphertexts_U[i][0]),
            int(ciphertexts_U[i][1]),
            int(ciphertexts_U[i][2])
        ])

    vec_a = [0] * len(ciphertexts_R)
    for i in range(len(ciphertexts_R)):
        vec_a[i] = current_hash % curve_order
        current_hash = hash_integers([current_hash])

    current_hash = hash_integers(
        [current_hash, int(A[0]),
         int(A[1]), int(A[2])])
    alpha = current_hash % curve_order

    current_hash = hash_integers([current_hash])
    beta = current_hash % curve_order

    gprod = 1
    for i in range(len(ciphertexts_R)):
        gprod = (gprod * (vec_a[i] + i * alpha + beta)) % curve_order

    A1 = crs_g[0]
    for i in range(1, n):
        A1 = add(A1, crs_g[i])
    A1 = multiply(A1, beta)
    A1 = add(A1, multiply(M, alpha))
    A1 = add(A1, A)

    start = timer()
    [current_hash, inner_prod_info] = gprod_verify(current_hash, crs[:], A1,
                                                   len(ciphertexts_R), gprod,
                                                   gprod_proof[:], n, logn)
    end = timer()
    print("gprod time = ", end - start)
    [vec_crs_h_exp, B, C, inner_prod, len_gprod] = inner_prod_info[:]

    start = timer()
    R = compute_multiexp(ciphertexts_R[:], vec_a[:])
    S = compute_multiexp(ciphertexts_S[:], vec_a[:])
    end = timer()

    print("R, S time = ", end - start)

    vec_gammas = [0] * num_blinders
    vec_deltas = [0] * num_blinders
    current_hash = hash_integers(
        [current_hash, int(A[0]),
         int(A[1]), int(A[2])])
    for i in range(num_blinders):
        current_hash = hash_integers([current_hash])
        vec_gammas[i] = current_hash % curve_order
        current_hash = hash_integers([current_hash])
        vec_deltas[i] = current_hash % curve_order

    (current_hash, b) = sameexp_verify(current_hash, R, S, crs_se1, crs_se2, T,
                                       U, sameexp_proof[:])

    if b != 1:
        print("VERIFICATION FAILURE: does not have same exponent")
        return 0

    for i in range(num_blinders):
        ciphertexts_T.append(multiply(crs_se1, vec_gammas[i]))
        ciphertexts_U.append(multiply(crs_se2, vec_deltas[i]))

    start = timer()
    [current_hash,
     b] = verify_multiexp_and_gprod_inner(current_hash, crs[:], vec_crs_h_exp,
                                          len_gprod, B, C, inner_prod,
                                          ciphertexts_T[:], ciphertexts_U[:],
                                          A, T, U, inner_proof[:], n, logn)
    end = timer()

    print("inner product time = ", end - start)

    if b != 1:
        print("VERIFICATION FAILURE: does not have correct inner product")
        return 0

    return 1
def multiexp_verify(current_hash, crs, ciphertexts_R, ciphertexts_S,
                    commit_exps, ip_proof, multiexp_R, multiexp_S, n, logn):
    [R, Rbl, Sbl, proof, last_exp] = ip_proof[:]

    ### Adding zero-knowledge

    current_hash = hash_integers([
        current_hash,
        int(R[0]),
        int(R[1]),
        int(R[2]),
        int(Rbl[0]),
        int(Rbl[1]),
        int(Rbl[2]),
        int(Sbl[0]),
        int(Sbl[1]),
        int(Sbl[2])
    ])
    x = current_hash % curve_order

    commit_exps = add(commit_exps, multiply(R, x))
    multiexp_R = add(multiexp_R, multiply(Rbl, x))
    multiexp_S = add(multiexp_S, multiply(Sbl, x))

    vec_crs_exp = [1] * n
    n_var = n

    for j in range(logn):
        n_var = n_var // 2
        ## print("i = ", i, "commit_exps = ", commit_exps)
        [zL, zR, CL, CR] = proof[j]

        current_hash = hash_integers([
            current_hash,
            int(zL[0][0]),
            int(zL[0][1]),
            int(zR[0][0]),
            int(zR[0][1]),
            int(CL[0]),
            int(CL[1]),
            int(CR[0]),
            int(CR[1])
        ])

        x = current_hash
        inv_x = inverse(x)

        for i in range(n):
            bin_i = int_to_binaryintarray(i, logn)
            if bin_i[logn - j - 1] == 1:
                vec_crs_exp[i] = (vec_crs_exp[i] * x) % curve_order

        multiexp_R = add(add(multiply(zL[0], x), multiexp_R),
                         multiply(zR[0], inv_x))
        multiexp_S = add(add(multiply(zL[1], x), multiexp_S),
                         multiply(zR[1], inv_x))

        commit_exps = add(add(multiply(CR, inv_x), commit_exps),
                          multiply(CL, x))

    crs_final = compute_multiexp(crs[:], vec_crs_exp[:])
    R_final = compute_multiexp(ciphertexts_R[:], vec_crs_exp[:])
    S_final = compute_multiexp(ciphertexts_S[:], vec_crs_exp[:])

    if add(multiply(crs_final, curve_order - last_exp),
           commit_exps) != (1, 1, 0):
        print("ERROR: final exponent is incorrect")
        return [current_hash, 0]

    if add(multiply(R_final, curve_order - last_exp), multiexp_R) != (1, 1, 0):
        print("ERROR: final ciphertext R is incorrect")
        return [current_hash, 0]

    if add(multiply(S_final, curve_order - last_exp), multiexp_S) != (1, 1, 0):
        print("ERROR: final ciphertext S is incorrect")
        return [current_hash, 0]

    return [current_hash, 1]
def multiexp_prove(current_hash, crs, ciphertexts_R, ciphertexts_S, exponents,
                   n, logn):
    proof = []

    ### Adding in zero knowledge
    vec_r = [0] * n
    for i in range(n):
        vec_r[i] = randbelow(curve_order)

    R = compute_multiexp(crs[:], vec_r[:])
    Rbl = compute_multiexp(ciphertexts_R[:], vec_r[:])
    Sbl = compute_multiexp(ciphertexts_S[:], vec_r[:])

    current_hash = hash_integers([
        current_hash,
        int(R[0]),
        int(R[1]),
        int(R[2]),
        int(Rbl[0]),
        int(Rbl[1]),
        int(Rbl[2]),
        int(Sbl[0]),
        int(Sbl[1]),
        int(Sbl[2])
    ])
    x = current_hash % curve_order

    for i in range(n):
        exponents[i] = (exponents[i] + vec_r[i] * x) % curve_order

    for j in range(logn):
        n = n // 2

        zL = [
            compute_multiexp(ciphertexts_R[n:], exponents[:n]),
            compute_multiexp(ciphertexts_S[n:], exponents[:n])
        ]
        zR = [
            compute_multiexp(ciphertexts_R[:n], exponents[n:]),
            compute_multiexp(ciphertexts_S[:n], exponents[n:])
        ]
        CL = compute_multiexp(crs[n:], exponents[:n])
        CR = compute_multiexp(crs[:n], exponents[n:])

        proof.append([zL, zR, CL, CR])

        current_hash = hash_integers([
            current_hash,
            int(zL[0][0]),
            int(zL[0][1]),
            int(zR[0][0]),
            int(zR[0][1]),
            int(CL[0]),
            int(CL[1]),
            int(CR[0]),
            int(CR[1])
        ])

        x = current_hash % curve_order
        inv_x = inverse(x)

        for i in range(n):
            crs[i] = add(multiply(crs[n + i], x), crs[i])
            ciphertexts_R[i] = add(multiply(ciphertexts_R[n + i], x),
                                   ciphertexts_R[i])
            ciphertexts_S[i] = add(multiply(ciphertexts_S[n + i], x),
                                   ciphertexts_S[i])

        crs = crs[:n]
        ciphertexts_R = ciphertexts_R[:n]
        ciphertexts_S = ciphertexts_S[:n]

        for i in range(n):
            exponents[i] = (exponents[n + i] * inv_x +
                            exponents[i]) % curve_order

        exponents = exponents[:n]

    return [current_hash, [R, Rbl, Sbl, proof, exponents[0]]]
def prove_multiexp_and_gprod_inner(current_hash, crs, vec_b, vec_c, inner_prod,
                                   ciphertexts_1, ciphertexts_2, vec_exp, n,
                                   logn):
    [crs_g, crs_h_scaled, u] = crs[:]
    crs_h = crs_g[:]

    proof = []

    ### Adding in zero knowledge
    vec_rgp = [0] * n
    vec_sgp = [0] * n
    vec_rme = [0] * n
    for i in range(n):
        vec_rgp[i] = randbelow(curve_order)
        vec_sgp[i] = randbelow(curve_order)
        vec_rme[i] = randbelow(curve_order)

    Rgp = compute_multiexp(crs_g[:], vec_rgp[:])
    Sgp = compute_multiexp(crs_h_scaled[:], vec_sgp[:])
    blgp1 = (compute_innerprod(vec_b[:], vec_sgp[:]) +
             compute_innerprod(vec_c[:], vec_rgp[:])) % curve_order
    blgp2 = compute_innerprod(vec_rgp[:], vec_sgp[:])

    Rme = compute_multiexp(crs_h[:], vec_rme[:])
    Bl1me = compute_multiexp(ciphertexts_1[:], vec_rme[:])
    Bl2me = compute_multiexp(ciphertexts_2[:], vec_rme[:])

    zkinfo = [Rgp, Sgp, Rme, blgp1, blgp2, Bl1me, Bl2me]

    current_hash = hash_integers([
        current_hash,
        int(Rgp[0]),
        int(Rgp[1]),
        int(Rgp[2]),
        int(Sgp[0]),
        int(Sgp[1]),
        int(Sgp[2]), blgp1, blgp2,
        int(Rme[0]),
        int(Rme[1]),
        int(Rme[2]),
        int(Bl1me[0]),
        int(Bl1me[1]),
        int(Bl1me[2]),
        int(Bl2me[0]),
        int(Bl2me[1]),
        int(Bl2me[2])
    ])
    x = current_hash % curve_order

    inner_prod = (inner_prod + blgp1 * x + blgp2 * x**2) % curve_order
    for i in range(n):
        vec_b[i] = (vec_b[i] + vec_rgp[i] * x) % curve_order
        vec_c[i] = (vec_c[i] + vec_sgp[i] * x) % curve_order
        vec_exp[i] = (vec_exp[i] + vec_rme[i] * x) % curve_order

    ### Inserting inner_prod into exponent.
    current_hash = hash_integers([current_hash, inner_prod])
    x = current_hash % curve_order
    u = multiply(u, x)

    for j in range(logn):
        n = n // 2

        zLgp = compute_innerprod(vec_b[n:], vec_c[:n])
        zRgp = compute_innerprod(vec_b[:n], vec_c[n:])
        zLme = [
            compute_multiexp(ciphertexts_1[n:], vec_exp[:n]),
            compute_multiexp(ciphertexts_2[n:], vec_exp[:n])
        ]
        zRme = [
            compute_multiexp(ciphertexts_1[:n], vec_exp[n:]),
            compute_multiexp(ciphertexts_2[:n], vec_exp[n:])
        ]

        CLgp_b = add(compute_multiexp(crs_g[:n], vec_b[n:]), multiply(u, zLgp))
        CLgp_c = compute_multiexp(crs_h_scaled[n:], vec_c[:n])

        CRgp_b = add(compute_multiexp(crs_g[n:], vec_b[:n]), multiply(u, zRgp))
        CRgp_c = compute_multiexp(crs_h_scaled[:n], vec_c[n:])
        CLme = compute_multiexp(crs_h[n:], vec_exp[:n])
        CRme = compute_multiexp(crs_h[:n], vec_exp[n:])

        proof.append([CLgp_b, CLgp_c, CRgp_b, CRgp_c, zLme, zRme, CLme, CRme])

        current_hash = hash_integers([
            current_hash,
            int(CLgp_b[0]),
            int(CLgp_b[1]),
            int(CLgp_b[2]),
            int(CLgp_c[0]),
            int(CLgp_c[1]),
            int(CLgp_c[2]),
            int(CRgp_b[0]),
            int(CRgp_b[1]),
            int(CRgp_b[2]),
            int(CRgp_c[0]),
            int(CRgp_c[1]),
            int(CRgp_c[2]),
            int(zLme[0][0]),
            int(zLme[0][1]),
            int(zLme[0][2]),
            int(zRme[0][0]),
            int(zRme[0][1]),
            int(zRme[0][2]),
            int(CLme[0]),
            int(CLme[1]),
            int(CLme[2]),
            int(CRme[0]),
            int(CRme[1]),
            int(CRme[2])
        ])

        x = current_hash % curve_order
        inv_x = inverse(x)

        for i in range(n):
            crs_g[i] = add(multiply(crs_g[n + i], inv_x), crs_g[i])
            crs_h_scaled[i] = add(multiply(crs_h_scaled[n + i], x),
                                  crs_h_scaled[i])
            vec_b[i] = (vec_b[i] + x * vec_b[n + i]) % curve_order
            vec_c[i] = (vec_c[i] + inv_x * vec_c[n + i]) % curve_order

            crs_h[i] = add(multiply(crs_h[n + i], x), crs_h[i])
            ciphertexts_1[i] = add(multiply(ciphertexts_1[n + i], x),
                                   ciphertexts_1[i])
            ciphertexts_2[i] = add(multiply(ciphertexts_2[n + i], x),
                                   ciphertexts_2[i])

            vec_exp[i] = (vec_exp[n + i] * inv_x + vec_exp[i]) % curve_order

        crs_g = crs_g[:n]
        crs_h_scaled = crs_h_scaled[:n]
        vec_b = vec_b[:n]
        vec_c = vec_c[:n]

        crs_h = crs_h[:n]
        ciphertexts_1 = ciphertexts_1[:n]
        ciphertexts_2 = ciphertexts_2[:n]

        vec_exp = vec_exp[:n]

    final_values = [vec_b[0], vec_c[0], vec_exp[0]]
    current_hash = hash_integers(
        [current_hash, vec_b[0], vec_c[0], vec_exp[0]])

    return [current_hash, [zkinfo[:], proof[:], final_values[:]]]
def verify_multiexp_and_gprod_inner(current_hash, crs, vec_crs_h_exp,
                                    len_gprod, B, C, inner_prod, ciphertexts_1,
                                    ciphertexts_2, commit_exps, multiexp_1,
                                    multiexp_2, inner_proof, n, logn):
    [crs_g, u, crs_se1, crs_se2] = crs[:]

    ### Adding in zero knowledge
    [zkinfo, proof, final_values] = inner_proof[:]
    [Rgp, Sgp, Rme, blgp1, blgp2, Bl1me, Bl2me] = zkinfo[:]

    current_hash = hash_integers([
        current_hash,
        int(Rgp[0]),
        int(Rgp[1]),
        int(Rgp[2]),
        int(Sgp[0]),
        int(Sgp[1]),
        int(Sgp[2]), blgp1, blgp2,
        int(Rme[0]),
        int(Rme[1]),
        int(Rme[2]),
        int(Bl1me[0]),
        int(Bl1me[1]),
        int(Bl1me[2]),
        int(Bl2me[0]),
        int(Bl2me[1]),
        int(Bl2me[2])
    ])
    x = current_hash % curve_order

    inner_prod = (inner_prod + blgp1 * x + blgp2 * x**2) % curve_order
    B = add(B, multiply(Rgp, x))
    C = add(C, multiply(Sgp, x))
    commit_exps = add(commit_exps, multiply(Rme, x))
    multiexp_1 = add(multiexp_1, multiply(Bl1me, x))
    multiexp_2 = add(multiexp_2, multiply(Bl2me, x))

    ### Putting inner_prod into exponent.
    current_hash = hash_integers([current_hash, inner_prod])
    x = current_hash % curve_order

    u = multiply(u, x)
    B = add(B, multiply(u, inner_prod))

    [final_b, final_c, final_exp] = final_values[:]
    vec_crs_g_exp = [final_b] * n
    vec_crs_h_shifted = [1] * n

    n_var = n
    for j in range(logn):
        n_var = n_var // 2

        [CLgp_b, CLgp_c, CRgp_b, CRgp_c, zLme, zRme, CLme, CRme] = proof[j]

        current_hash = hash_integers([
            current_hash,
            int(CLgp_b[0]),
            int(CLgp_b[1]),
            int(CLgp_b[2]),
            int(CLgp_c[0]),
            int(CLgp_c[1]),
            int(CLgp_c[2]),
            int(CRgp_b[0]),
            int(CRgp_b[1]),
            int(CRgp_b[2]),
            int(CRgp_c[0]),
            int(CRgp_c[1]),
            int(CRgp_c[2]),
            int(zLme[0][0]),
            int(zLme[0][1]),
            int(zLme[0][2]),
            int(zRme[0][0]),
            int(zRme[0][1]),
            int(zRme[0][2]),
            int(CLme[0]),
            int(CLme[1]),
            int(CLme[2]),
            int(CRme[0]),
            int(CRme[1]),
            int(CRme[2])
        ])

        x = current_hash % curve_order
        inv_x = inverse(x)

        for i in range(n):
            bin_i = int_to_binaryintarray(i, logn)
            if bin_i[logn - j - 1] == 1:
                vec_crs_g_exp[i] = (vec_crs_g_exp[i] * inv_x) % curve_order
                vec_crs_h_shifted[i] = (vec_crs_h_shifted[i] * x) % curve_order

        B = add(add(multiply(CRgp_b, inv_x), B), multiply(CLgp_b, x))
        C = add(add(multiply(CRgp_c, inv_x), C), multiply(CLgp_c, x))
        multiexp_1 = add(add(multiply(zLme[0], x), multiexp_1),
                         multiply(zRme[0], inv_x))
        multiexp_2 = add(add(multiply(zLme[1], x), multiexp_2),
                         multiply(zRme[1], inv_x))

        commit_exps = add(add(multiply(CRme, inv_x), commit_exps),
                          multiply(CLme, x))

    ciphertexts_1_final = compute_multiexp(ciphertexts_1[:],
                                           vec_crs_h_shifted[:])
    ciphertexts_2_final = compute_multiexp(ciphertexts_2[:],
                                           vec_crs_h_shifted[:])

    if add(multiply(ciphertexts_1_final, curve_order - final_exp),
           multiexp_1) != (1, 1, 0):
        print("ERROR: final ciphertext 1 is incorrect")
        return [current_hash, 0]

    if add(multiply(ciphertexts_2_final, curve_order - final_exp),
           multiexp_2) != (1, 1, 0):
        print("ERROR: final ciphertext 2 is incorrect")
        return [current_hash, 0]

    current_hash = hash_integers([current_hash, final_b, final_c, final_exp])
    x = current_hash % curve_order

    vec_crs_h_exp[0] = (x**2 * vec_crs_g_exp[0] +
                        x * vec_crs_h_shifted[0] * final_exp +
                        vec_crs_h_exp[0] * vec_crs_h_shifted[len_gprod - 1] *
                        final_c) % curve_order
    for i in range(1, len_gprod):
        vec_crs_h_exp[i] = (x**2 * vec_crs_g_exp[i] +
                            x * vec_crs_h_shifted[i] * final_exp +
                            vec_crs_h_exp[i] * vec_crs_h_shifted[i - 1] *
                            final_c) % curve_order

    for i in range(len_gprod, n):
        vec_crs_h_exp[i] = (
            x**2 * vec_crs_g_exp[i] + x * vec_crs_h_shifted[i] * final_exp +
            vec_crs_h_exp[i] * vec_crs_h_shifted[i] * final_c) % curve_order

    inner_prod = final_b * final_c % curve_order

    expected_outcome = compute_multiexp(crs_g[:], vec_crs_h_exp[:])
    expected_outcome = add(expected_outcome,
                           multiply(u, (inner_prod * x**2) % curve_order))
    expected_outcome = add(expected_outcome,
                           multiply(commit_exps, curve_order - x))
    expected_outcome = add(expected_outcome, multiply(C, curve_order - 1))
    expected_outcome = add(expected_outcome, multiply(B,
                                                      (-x**2) % curve_order))

    if expected_outcome != Z1:
        print("ERROR: final exponent is incorrect")
        return [current_hash, 0]

    return [current_hash, 1]