Loading [MathJax]/jax/output/HTML-CSS/jax.js

Xavier Pinho

Discrete logarithm: Silver-Pohlig-Hellman algorithm

Introduction

We consider the following instance of the so-called discrete logarithm problem:

Let G be a finite cyclic group generated by α with order N. Let βG. Find xZN (notation: logαβ) such that αx=β.

Let us first recall that:

Therefore, one naive way to compute logαβ is to multiply α by itself as many times as needed to obtain β.

# The naive algorithm implemented in SageMath
def dlog_naive(a,b):
  (n,x)=(0,1)
  while not x == b:
      n+=1
      x*=a
  return a

Example. Consider the group Z251 whilst generated by 6. Let us determine

n=log6184i.e.n such that 6n184(mod251)

According to the naive algorithm, we test the hypothesis

n=0,n=1,,n=249

sequentially until 6n184(mod251). Such will happen by the 229th iteration, meaning that

n=log6184=229i.e.6229184(mod251)

Albeit straightforward to implement, this algorithm has the clear disadvantage of having time complexity O(N), which makes it inviable for sufficiently large group orders.

# Example above encoded in SageMath using the naive algorithm
Z=Zmod(251)
a=Z(6)          # or Z.multiplicative_generator()
b=Z(184)
dlog_naive(a,b) # = 229

Silver-Pohlig-Hellman

The algorithm we present next (due to. R. Silver, S. Pohlig and M. Hellman) is reminiscent of the saying divide and conquer, in that it divides the discrete logarithm problem over a group into the discrete logarithm problem of its subgroups.

Let G be a finite cyclic group with order N generated by α, and let βG. Let l=logαβ and recall that l is determined modulo N. We wish to determine l.

Suppose that N=ki=1peii is the prime factorization of N. Then, by the Chinese Remainder Theorem (CRT) it is enough to determine xi (1ik) in {lx1(modpe11)lx2(modpe22)lxk(modpekk) in order to determine l modulo N.

We now have the concern of finding xi in lx1(modpeii) for each i. Let us simplify the explanation by choosing some i (1ik) and then defining p:=pi,e:=ei,x:=xi.

By definition, lx(modpe) means l=x+spe for some sZ. We are going to find x by finding its base-p digits one at a time. The base-p expansion of x is x=(de1d1d0)p=e1i=0dipi where di{0,,p1}.

Regarding d0, we start by observing the following auxiliary result Npl=Np(x+spe)=Np(d0+d1p++de1pe1+spe)=Np(d0+Kp)for some KZ=Npd0+NKfor some KZ which implies that NplNpd0(modN).

Using (5) we may conclude that βNp=(αl)Np=(αNp)d0.

Hence, regarding the (unique) subgroup of G with order p, say Gp, for which αNp is a generator, we have d0=log¯α¯β,where ¯α=αNp and ¯β=βNp.

Hopefully, p is now small enough for us to be able to compute d0 according to the naive algorithm.

Regarding d1 we similarly observe that Np2(ld0)=Np2(xd0+spe)=Np2(d1p++de1pe1+spe)=Np(d1++de1pe1+spe1)=Np(d1+Kp)for some KZ=Npd1+NKfor some KZ which implies that Np2(ld0)Npd1(modN).

Using (8) we may conclude that (αNp)d1=(αld0)Np2=(αlαd0)Np2=(βαd0)Np2 and therefore d1=log¯α¯β,where ¯α=αNp and ¯β=(βαd0)Np2.

Regarding the general case of the j-th digit (0je1) we have Npj+1(l(d0+d1p++dj1pj1))=Npj+1(x(d0+d1p++dj1pj1)+spe)=Npj+1(djpj++de1pe1+spe)=Npj+1(djpj+Kpj+1)for some KZ=Npj+1djpj+NKfor some KZ which implies that Npj+1(l(d0+d1p++dj1pj1))Npdj(modN).

Using (11) we may conclude that (αNp)dj=(αlj1i=0dipi)Npj+1=(αlαj1i=0dipi)Npj+1=(βαj1i=0dipi)Npj+1 and, hence, dj=log¯α¯β,where ¯α=αNp and ¯β=(βαj1i=0dipi)Npj+1

Once we have found all di from (4), we have found x from (3). We repeat this process until we find all xi from (2). Then, all that is left is to apply the Chinese Remainder Theorem and obtain the desired result, l.

Implementation

For implementation purposes, it is useful to observe the following recurrence relation regarding ¯β. Let ¯βj(0je1) be the variable ¯β when finding the j-th digit. Define

β0:=ββj+1:=βjαdjpj

Then, ¯βj=(βj)Npj+1.

# The Silver-Pohlig-Hellman algorithm implemented in SageMath
def dlog_sph(a,b):
    N=a.multiplicative_order()
    P=factor(N)
    k=len(P)
    (xi,pe)=([],[])
    for i in xrange(0,k):
        (p,e)=P[i]
        d=[]
        a_=a^(N/p)
        bj=b
        for j in xrange(0,e):
            b_=(bj)^(N/(p^(j+1)))
            d.append(dlog_naive(a_,b_))
            bj=bj*a^(-d[j]*p^j)
        xi.append(ZZ(d,p))
        pe.append(p^e)
    return CRT_list(xi,pe)

Conclusion

The Silver-Pohlig-Hellman algorithm splits an instance of the discrete logarithm problem into many smaller instances which are hopefully amenable to be solved according to the naive algorithm—or any other algorithm for that matter, e.g. Shanks’ “baby-step giant-step”. This makes it a viable algorithm if the order of the group at hand is factorizable into small enough integers. With that in mind, it should be used together with a specialised factorization algorithm for finding small factors, like ρ-Pollard.

References