BFV in base R

Note that this vignette follows the exact same structure as BFV, and the variables have the same values.

The functions that wrapped several steps in the BFV vignette, are simply unwrapped here for the reader to see what is going on under the hood.

Load libraries that will be used.

library(polynom)
library(HomomorphicEncryption)

Set some parameters.

d  =   4
n  =   2^d
p  =   (n/2)-1
q  = 874
pm = GenPolyMod(n)

Set a working seed for random numbers

set.seed(123)

Create the secret key:

# generate a secret key
s = polynomial( sample( c(-1,0,1), n, replace=TRUE ) )
print(s)
#> 1 + x + x^2 + x^4 + x^8 - x^9 - x^12 + x^14 - x^15

We randomly draw from the set [-1, 0, 1] with replacement n values. These integers are used as the parameters of the polynomial used as the secret key.

Create and the polynomial a, which will go into the public key:

# generate a
a = polynomial(sample.int(q, n, replace=TRUE))
print(a)
#> 91 + 348*x + 649*x^2 + 355*x^3 + 840*x^4 + 26*x^5 + 519*x^6 + 426*x^7 + 649*x^8  
#> + 766*x^9 + 211*x^10 + 590*x^11 + 593*x^12 + 555*x^13 + 871*x^14 + 373*x^15

For the polynomial a, we sample from positive integers up to q, n values, with replacement.

Create and the polynomial e, which will go into the public key:

# generate the error
e = polynomial( coef=round(stats::rnorm(n, 0, n/3)) )
print(e)
#> -4 - x - 2*x^2 - 6*x^3 + 6*x^5 - x^6 - 6*x^7 - 4*x^8 + 4*x^9 - 2*x^10 - 7*x^11  
#> - 3*x^12 - x^13 + 5*x^14 - x^15

Some error noise is generated, with values bounded by 1/3 of n.

Generate the public key part 0.

# generate the public key
temp = -(a*s + e)
temp = temp %% pm
pk0 = CoefMod(temp, q)
print(pk0)
#> 560 + 287*x + 70*x^2 + 788*x^3 + 534*x^4 + 150*x^5 + 43*x^6 + 331*x^7 + 328*x^8  
#> + 318*x^9 + 184*x^10 + 519*x^11 + 504*x^12 + 783*x^13 + 79*x^14 + 425*x^15

Generate public key part 1.

pk1 = a

Part 1 of the public key is simply equal to a.

Create a polynomial message

# create a message
m = polynomial( coef=c(6, 4, 2) )
print(m)
#> 6 + 4*x + 2*x^2

Create error polynomials for the encryption

# polynomials for encryption
e1 = polynomial( coef=round(stats::rnorm(n, 0, n/3)) )
e2 = polynomial( coef=round(stats::rnorm(n, 0, n/3)) )

Create the term u for encryption.

u  = polynomial( sample( c(-1,0,1), (n-1), replace=TRUE) )
print(u)
#> x^3 - x^5 + x^9 + x^11 + x^13 - x^14

Generate the ciphertext part 0.

temp = pk0 * u + e1 + floor(q/p) * m
temp = temp %% pm
ct0 = CoefMod(temp, q)
print(ct0)
#> 157 + 787*x + 337*x^2 + 236*x^3 + 454*x^4 + 575*x^5 + 87*x^6 + 14*x^7 + 448*x^8  
#> + 640*x^10 + 747*x^11 + 711*x^12 + 564*x^13 + 866*x^14 + 678*x^15

Generate the ciphertext part 0.

temp = pk1 * u + e2
temp = temp %% pm
ct1 = CoefMod(temp, q)
print(ct1)
#> 760 + 698*x + 679*x^2 + 477*x^3 + 329*x^4 + 414*x^5 + 487*x^6 + 165*x^7 +  
#> 111*x^8 + 642*x^9 + 409*x^10 + 565*x^11 + 660*x^12 + 644*x^13 + 469*x^14 +  
#> 297*x^15

Decrypt

temp = (ct1 * s) + ct0
temp = temp %% pm
temp = CoefMod(temp, q)

# rescale
temp = temp * p/q

Round (remove the error) then mod p

# round then mod p
decrypt = CoefMod(round(temp), p)
print(decrypt)
#> 6 + 4*x + 2*x^2