ElGamal.mws

El Gamal

İMike May, S.J., 2002, maymk@slu.edu

This worksheet walks through the use of El Gamal, both as a public key cryptographic system and as a means of signing documents.

>    restart;

Basic El Gamal for message encryption.

To set up El Gamal we want to choose a large prime and a generator.

>    p := nextprime(rand(1..10^35)());

p := 27419669081321110693270343633073797

>    p := 27419669081321110693270343633073797;

p := 27419669081321110693270343633073797

p = 27419669081321110693270343633073797.

In order to find a generator, we want to factor p-1.

>    ifactor(p-1);

``(2)^2*``(23)*``(298039881318707724926851561229063)

We see that p-1 has 3 primes in its factorization.

>    q1 := 2: q2 := 23: q3 := 298039881318707724926851561229063:

To test if x is a generator, we check that the order of x does not divide (p-1)/q for each prime q that factors p.

>    gentest := x ->
   [Power(x,(p-1)/q1) mod p, Power(x,(p-1)/q2) mod p,
    Power(x,(p-1)/q3) mod p]:

>    gentest(2);
gentest(3);
gentest(5);

[27419669081321110693270343633073796, 4824105265268801297832593247242833, 4951760157141521099596496896]

[27419669081321110693270343633073796, 7226018751726945663791091023814196, 14736610876956489598595647093978809]

[27419669081321110693270343633073796, 5591650270261883327344412931520746, 5425344836564900966653959433524156]

Thus we see that 2, 3, and 5 are generators of Z_p.  Let alpha = 5.

>    alpha := 5;

alpha := 5

Now we choose a secret exponent a, and compute beta = alpha^a. and publish (p, alpha, beta)

>    a := rand(10..p-10)();

a := 8121769181099576933380904991576322

>    a := 8121769181099576933380904991576322;

a := 8121769181099576933380904991576322

>    beta := Power(alpha, a) mod p;

beta := 17907006995302211768060958454599666

We now publish our public key, [p, alpha, beta].

>    publicKey := [p, alpha, beta];

publicKey := [27419669081321110693270343633073797, 5, 17907006995302211768060958454599666]

To send the message "fred", Bob first turns the message into a number.  (He uses the simple rule turning a to 01, b to 02, ..., z to 26.)

>    message := 6180504;

message := 6180504

Bob then randomly chooses k1 and computes r1 and t1 which are sent to us

>    k1 := rand(10..p-10)();

k1 := 4642553004401863428498260672540153

>    k1 := 4642553004401863428498260672540153;

k1 := 4642553004401863428498260672540153

>    r1 := Power(alpha,k1) mod p;

r1 := 5901804098614052670020408692065785

>    t1 := (Power(beta,k1) mod p)*(message) mod p;

t1 := 1956800570713478481188664797452267

>    sent := [r1,t1];

sent := [5901804098614052670020408692065785, 1956800570713478481188664797452267]

We decrypt in the usual fashon and recogn1ze the message as fred.

>    received := sent[2]*(Power(1/sent[1],a) mod p) mod p;

received := 6180504

El Gamal for digital signatures

Suppose instead of receiving the mesage fred in secret, we want to send the message "fred" signed.

Recall the public key and the private key a.

>    myTotKey := [p, alpha, beta, a];

myTotKey := [27419669081321110693270343633073797, 5, 17907006995302211768060958454599666, 8121769181099576933380904991576322]
myTotKey := [27419669081321110693270343633073797, 5, 17907006995302211768060958454599666, 8121769181099576933380904991576322]

We choose a random k2

>    k2 := rand(10..p-10)();

k2 := 5248972213589823234356031982495167

>    k2 := 5248972213589823234356031982495167;

k2 := 5248972213589823234356031982495167

We want k2 to be relatively prime to p-1, so we find the gcd and divide it out.

>    comFact := igcd(k2, p-1);
k2 := k2/comFact;

comFact := 1

k2 := 5248972213589823234356031982495167

Compute r2 and s2.

>    r2 := Power(alpha, k2) mod p;

r2 := 7394797045453427865921953194931203

>    s2 := (message - r2*a)/k2 mod (p-1);

s2 := 24123169111518353846613958314732594

I send (message, r2, s2).

>    signed := [message,r2, s2];

signed := [6180504, 7394797045453427865921953194931203, 24123169111518353846613958314732594]

To verify, I compute v1 and v2 and see they are the same.

>    v1 := (Power(beta,signed[2]) mod p)*
      (Power(signed[2],signed[3]) mod p) mod p;

v1 := 26211350169610375439529822927530968

>    v2 := Power(alpha, message) mod p;

v2 := 26211350169610375439529822927530968

Finding elements of high order

There is an obvious catch if the use of El Gamal as described above.  To find a generator of Z_p, we need to factor p-1.  But for El Gamal to be secure p should be big enough that factoring p-1 is not a tractible problem.  To fix this problem, it should be noted that we don't really need a generator, we simply need an element of large order.  A solution is to collect the small factors of p-1.  Anything raised to that power will have an order diving the remaining large factor of p-1.

Consider how this works with a prime of 200 digits.

>    p := nextprime(rand(1..10^200)());

p := 7949045703916959416008843057167496049883408581292045791645374701946164403139530792062494734995105353008614648630719815559076346642939267370952542851097327260060898121976009937467598293376684547350...
p := 7949045703916959416008843057167496049883408581292045791645374701946164403139530792062494734995105353008614648630719815559076346642939267370952542851097327260060898121976009937467598293376684547350...

>    p:= 79490457039169594160088430571674960498834085812920457916453747019461644031395307920624947349951053530086146486307198155590763466429392673709525428510973272600608981219760099374675982933766845473509941:

We first find the product of primes less than 1000.  These will be the small factors we eliminate.

>    smallPrimes := 2:
test := 2:
while (test < 1000) do
test := nextprime(test):
smallPrimes := smallPrimes*test:
od:
smallPrimes;

197666537108040751821438707719902384755390881421046725378975487769293843132272894978086192136285876624905956251474749433215322354927422041288469135377703228645729959749356541005942890568857461776741067...
197666537108040751821438707719902384755390881421046725378975487769293843132272894978086192136285876624905956251474749433215322354927422041288469135377703228645729959749356541005942890568857461776741067...
197666537108040751821438707719902384755390881421046725378975487769293843132272894978086192136285876624905956251474749433215322354927422041288469135377703228645729959749356541005942890568857461776741067...
197666537108040751821438707719902384755390881421046725378975487769293843132272894978086192136285876624905956251474749433215322354927422041288469135377703228645729959749356541005942890568857461776741067...

Using the gcd, we remove all the small prime factors of p-1.

>    k := p-1:
comFact := igcd(smallPrimes, k):
while (comFact > 1) do
k := k/comFact;
comFact := igcd(smallPrimes, k):
od:
k;

581759262676662667488754089246253646303482981337121281701846081680767745322169444740542622970137701970345206059769863210812298318853419564867398062523525821433713133952283129419987249041495799
581759262676662667488754089246253646303482981337121281701846081680767745322169444740542622970137701970345206059769863210812298318853419564867398062523525821433713133952283129419987249041495799

>    removedFactors := (p-1)/k;

removedFactors := 136638060

Now any element x^removedFactors will have order dividing k,

>    alpha := Power(2, removedFactors) mod p;

alpha := 675047033582126700534471867568574910736662964844472339644539880602203094519416648328628879083982844221219166226912198281292011175594792709400229143594790223935051151540988913565760395587910513...
alpha := 675047033582126700534471867568574910736662964844472339644539880602203094519416648328628879083982844221219166226912198281292011175594792709400229143594790223935051151540988913565760395587910513...

With high probability, alpha has order k.

>   

>