I am following the NTRUEncrypt cryptosystem as described on the wikipedia. I have implemented it in Sage Math engine (with small problems along the way, but in the end - succesfully resolved) and the system works just as expected.
I have noticed an interesting publication regarding expanding NTRU to higher order algebras. This one is about quaternions but in the following example I will try complex numbers first, baby steps.
So, I have implemented the code to define cryptosystem's parameters, encrypt and decrypt, but in the end I cannot recover the initial polynomial. I do not know if this is just a bug or is there a conceptual error in my steps; need some help loooking through this.
Define parameters and respective polynomial rings:
N = 11
p = 3
q = 37
R.<x> = PolynomialRing(ZZ)
RR = R.quotient(x^N - 1)
P.<x> = PolynomialRing(Zmod(p))
PP = P.quotient(x^N - 1)
Q.<x> = PolynomialRing(Zmod(q))
QQ = Q.quotient(x^N - 1)
Select polynomials $f$ and $g$
f_1 = RR([-1, 1, 1, 0, -1, 0, 1, 0, 0, 1, -1])
f_2 = RR([-1, 1, 1, 0, -1, 0, 1, 0, 0, 1, -1])
g_1 = RR([-1, 0, 1, 1, 0, 1, 0, 0, -1, 0, -1])
g_2 = RR([-1, 0, 1, 1, 0, 1, 0, 0, -1, 0, -1])
f = (f_1, f_2)
g = (g_1, g_2)
Calculate a multiplicative inverse of $f$ over respective rings $PP$ and $QQ$.
Remember that: $f^{-1}_{XX} = [\frac{1}{||f||^2}]_{XX}\times \bar{f}$
Also: $||f||^2 = f_0^2 + f_1^2$ and $\bar{f} = (f_0, -f_1)$
f_ = (f[0], -f[1])
f_norm = f[0]^2 + f[1]^2
f_norm_inv_p = PP(f_norm) ^ -1
fp = (
PP(f_[0] * f_norm_inv_p),
PP(f_[1] * f_norm_inv_p),
)
f_norm_inv_q = QQ(f_norm) ^ -1
fq = (
QQ(f_[0] * f_norm_inv_q),
QQ(f_[1] * f_norm_inv_q),
)
Check the inverses under complex multiplication $a \times b = (ac-bd, ad+bc)$
assert PP(f[0] * fp[0]) - PP(fp[1] * f[1]) == 1
assert PP(f[0] * fp[1]) + PP(f[1] * fp[0]) == 0
assert QQ(f[0] * fq[0]) - QQ(fq[1] * f[1]) == 1
assert QQ(f[0] * fq[1]) + QQ(f[1] * fq[0]) == 0
Generate public key $h = pf_q \times g$
pfq = (p * fq[0], p * fq[1])
h = (
QQ(pfq[0] * g[0]) - QQ(g[1] * pfq[1]),
QQ(pfq[0] * g[1]) + QQ(pfq[1] * g[0]),
)
Encryption $e = r \times h + m$
m = (
PP([0, 0, 0, 0, 0, 1, 1, 0, -1, 1, 1]),
PP([0, 0, 0, 0, 0, 1, 1, 0, -1, 1, 1]),
)
r = (
RR([0, 1, 1, 1, 1, 1, 1, 0, -1, 1, 1]),
RR([0, 1, 1, 1, 1, 1, 1, 0, -1, 1, 1]),
)
rh = (
r[0] * h[0] - h[1] * r[1],
r[0] * h[1] + r[1] * h[0],
)
e = (
QQ(rh[0] + m[0]),
QQ(rh[1] + m[1]),
)
Decryption:
$a = f \times e$ (remember to center lift the coefficients)
$b = PP[a]$
$c = f_p \times b$
a = (
QQ(f[0]) * e[0] - e[1] * QQ(f[1]),
QQ(f[0]) * e[1] + QQ(f[1]) * e[0],
)
a = (
ZZ['x']([coeff.lift_centered() for coeff in a[0].lift()]),
ZZ['x']([coeff.lift_centered() for coeff in a[1].lift()]),
)
b = (PP(a[0]), PP(a[1]))
c = (
fp[0] * b[0] - b[1] * fp[1],
fp[0] * b[1] + fp[1] * b[0],
)
In the end $c \neq m$... I am a little troubled, why(?)
P.S. To quickly check Sage piece of code you may use this site: https://sagecell.sagemath.org/