// // Discrete logarithm calculator // // Written by Dario Alejandro Alpern (Buenos Aires - Argentina) // Last updated April 15th, 2005, See http://www.alpertron.com.ar/DILOG.HTM // // No part of this code can be used for commercial purposes without // the written consent from the author. Otherwise it can be used freely // except that you have to write somewhere in the code this header. // import java.applet.*; import java.util.*; import java.awt.*; import java.math.*; public final class logdi extends ecm { private TextField textBase, textPower, textMod, textExp, textPeriod; private Button btnSolve; private BigInteger base, power, modulus; private BigInteger LastModulus = BigInt0; private Frame factorWindow; private ecm factorPanel; private BigInteger PrimesMod[]; // Mod = Modulus. private int ExponentsMod[]; private BigInteger PrimesModBak[]; private int ExponentsModBak[]; private BigInteger PrimesGO[]; // GO = Group Order. private int ExponentsGO[]; private BigInteger PrimesGOBak[]; private int ExponentsGOBak[]; private BigInteger nbrV[]; private int NumberLengthOther; private int NbrFactorsMod=0; private long TestNbrOther[] = new long[NLen]; private long nbrA[] = new long[NLen]; private long nbrA2[] = new long[NLen]; private long nbrB[] = new long[NLen]; private long nbrB2[] = new long[NLen]; private long nbrK[] = new long[NLen]; private long nbrR[] = new long[NLen]; private long nbrROther[] = new long[NLen]; private long nbrR2[] = new long[NLen]; private long nbrPower[] = new long[NLen]; private long nbrBase[] = new long[NLen]; private double dNOther; public void init() { Label labelBase, labelPower, labelMod, labelExp, labelPeriod, labelBottom; PrimesMod=new BigInteger[400]; ExponentsMod=new int[400]; PrimesModBak=new BigInteger[400]; ExponentsModBak=new int[400]; PrimesGO=new BigInteger[400]; ExponentsGO=new int[400]; nbrV = new BigInteger[400]; setLayout(null); labelBase = new Label("Base"); labelBase.reshape(5, 15, 60, 14); labelBase.setFont(new Font("Courier", Font.PLAIN, 12)); labelBase.setAlignment(Label.CENTER); add(labelBase); textBase = new TextField(64); textBase.reshape(70, 10, 510, 30); textBase.setEditable(true); add(textBase); labelPower = new Label("Potencia"); labelPower.reshape(5, 65, 60, 14); labelPower.setFont(new Font("Courier", Font.PLAIN, 12)); labelPower.setAlignment(Label.CENTER); add(labelPower); textPower = new TextField(64); textPower.reshape(70, 60, 510, 30); textPower.setEditable(true); add(textPower); labelMod = new Label("Módulo"); labelMod.reshape(5, 115, 60, 14); labelMod.setFont(new Font("Courier", Font.PLAIN, 12)); labelMod.setAlignment(Label.CENTER); add(labelMod); textMod = new TextField(64); textMod.reshape(70, 110, 510, 30); textMod.setEditable(true); add(textMod); btnSolve = new Button("Hallar logaritmo discreto"); btnSolve.reshape(210, 160, 180, 30); btnSolve.setFont(new Font("Courier", Font.PLAIN, 12)); add(btnSolve); labelStatus = new Label(""); labelStatus.reshape(30, 205, 520, 14); labelStatus.setFont(new Font("Courier", Font.PLAIN, 12)); labelStatus.setAlignment(Label.CENTER); add(labelStatus); textExp = new TextField(64); labelExp = new Label("Expon"); labelExp.reshape(5, 235, 60, 14); labelExp.setFont(new Font("Courier", Font.PLAIN, 12)); labelExp.setAlignment(Label.CENTER); add(labelExp); textExp = new TextField(64); textExp.reshape(70, 230, 510, 30); textExp.setEditable(false); add(textExp); textPeriod = new TextField(64); labelPeriod = new Label("Período"); labelPeriod.reshape(5, 285, 60, 14); labelPeriod.setFont(new Font("Courier", Font.PLAIN, 12)); labelPeriod.setAlignment(Label.CENTER); add(labelPeriod); textPeriod = new TextField(64); textPeriod.reshape(70, 280, 510, 30); textPeriod.setEditable(false); add(textPeriod); labelBottom = new Label("Escrito por Dario Alejandro Alpern. Actualizado el 15 de abril de 2005"); labelBottom.reshape(10, 330, 570, 14); labelBottom.setFont(new Font("Courier", Font.PLAIN, 12)); labelBottom.setAlignment(Label.CENTER); add(labelBottom); validate(); textBase.requestFocus(); } public boolean handleEvent(Event e) { if (e.id == Event.ACTION_EVENT && e.target == btnSolve || e.id == Event.KEY_PRESS && e.key == 13) { if (calcThread != null) { TerminateThread = true; try { calcThread.join(); /* Wait until the solving thread dies */ } catch (InterruptedException ie) {}; } calcThread = new Thread(this); /* Start solving thread */ calcThread.start(); return true; } if (e.id == Event.KEY_PRESS && e.key == Event.TAB) { if (e.target == textBase) {textPower.requestFocus();} if (e.target == textPower) {textMod.requestFocus();} if (e.target == textMod) {textBase.requestFocus();} return true; } return super.handleEvent(e); } public void run() { BigInteger groupOrder, subGroupOrder, range, powSubGroupOrder; BigInteger GroupOrder = null; BigInteger Exponent, runningExp, baseExp, mod; BigInteger basePH, powerPH; BigInteger logar=null, logarMult; BigInteger divideExp, nbrD; BigInteger bigNbrA2, bigNbrB2; BigInteger DiscreteLog, DiscreteLogPeriod; int d, j, indexBase, indexExp; int i, index; long addA, addB, addA2, addB2; long mult1, mult2; long magnitude; int mostSignificantDword, leastSignificantDword; long firstLimit, secondLimit; long brentK, brentR; int ExpressionRC; BigInteger ExpressionResult[] = new BigInteger[1]; Old=System.currentTimeMillis(); OldTimeElapsed = 0; lModularMult = 0; TerminateThread = false; try { try { ExpressionRC = expression.ComputeExpression(textMod.getText().trim(),1,ExpressionResult); } catch (OutOfMemoryError e) { textExp.setText("Sin memoria."); return; } catch (ArithmeticException e) { return; } modulus= ExpressionResult[0].abs(); if (ExpressionRC != 0) { textExp.setText("Módulo: "+expressionText[-1-ExpressionRC]); return; } try { ExpressionRC = expression.ComputeExpression(textBase.getText().trim(),1,ExpressionResult); } catch (OutOfMemoryError e) { textExp.setText("Sin memoria."); return; } catch (ArithmeticException e) { return; } base = ExpressionResult[0].mod(modulus); if (ExpressionRC != 0) { textExp.setText("Base: "+expressionText[-1-ExpressionRC]); return; } try { ExpressionRC = expression.ComputeExpression(textPower.getText().trim(),1,ExpressionResult); } catch (OutOfMemoryError e) { textExp.setText("Sin memoria."); return; } catch (ArithmeticException e) { return; } power = ExpressionResult[0].abs(); if (ExpressionRC != 0) { textExp.setText("Potencia: "+expressionText[-1-ExpressionRC]); return; } } catch (Exception e) { textExp.setText("Datos inválidos"); return; } if (modulus.gcd(base).equals(BigInt1) == false) { textExp.setText("El módulo y la base no son primos entre si."); return; } if (modulus.gcd(power).equals(BigInt1) == false) { textExp.setText("El módulo y la potencia no son primos entre si."); return; } if (modulus.compareTo(BigInt1) <= 0) { textExp.setText("El módulo debe ser mayor que 1."); return; } if (LastModulus.equals(modulus) == false) { if (GetSmallFactors(modulus, PrimesModBak, ExponentsModBak, Typ, 0) != 1) { LastModulus = modulus; factorPanel = new ecm(); factorWindow = new Frame("Factorización del módulo"); factorWindow.setResizable(false); factorWindow.add(factorPanel); factorPanel.setSize(600, 390); Insets in = factorWindow.getInsets(); factorWindow.setSize(600+in.right+in.left, 390+in.top+in.bottom); factorWindow.show(); NbrFactorsMod = factorPanel.getFactors(modulus, PrimesModBak, ExponentsModBak); factorWindow.remove(factorPanel); factorWindow.dispose(); } else { NbrFactorsMod = NbrFactors; } for (j=0; j<NbrFactorsMod; j++) { PrimesMod[j] = PrimesModBak[j]; ExponentsMod[j] = ExponentsModBak[j]; } } DiscreteLog = BigInt0; DiscreteLogPeriod = BigInt1; for (index = 0; index < NbrFactorsMod; index++) { groupOrder = PrimesMod[index].subtract(BigInt1); textExp.setText("Calculando el logaritmo discreto..."); if (GetSmallFactors(groupOrder, PrimesGO, ExponentsGO, Typ, 0) != 1) { LastModulus = modulus; factorPanel = new ecm(); factorWindow = new Frame("Factorización del Group order"); factorWindow.setResizable(false); factorWindow.add(factorPanel); factorPanel.setSize(600, 390); Insets in = factorWindow.getInsets(); factorWindow.setSize(600+in.right+in.left, 390+in.top+in.bottom); factorWindow.show(); NbrFactors = factorPanel.getFactors(groupOrder, PrimesGO, ExponentsGO); factorWindow.remove(factorPanel); factorWindow.dispose(); } mod = PrimesMod[index]; range = mod.divide(BigInt3); logar = BigInt0; logarMult = BigInt1; BigNbrToBigInt(mod); yieldFreq = 1000000/(NumberLength*NumberLength); GetMontgomeryParms(); if (NumberLength == 1) { mostSignificantDword = NLen - 1; leastSignificantDword = NumberLength - 1; } else { mostSignificantDword = NumberLength - 1; leastSignificantDword = NumberLength - 2; } secondLimit = ((TestNbr[mostSignificantDword] << 31) + TestNbr[leastSignificantDword]) * 2 / 3; firstLimit = secondLimit / 2; for (indexBase = 0; indexBase < NbrFactors; indexBase++) { textExp.setText("Calculando el logaritmo discreto en un subgrupo de "+PrimesGO[indexBase]+" elementos."); runningExp = BigInt0; subGroupOrder = PrimesGO[indexBase]; ExchangeMods(); // TestNbr <- subGroupOrder BigNbrToBigInt(subGroupOrder); dN = (double)TestNbr[NumberLength-1]; if (NumberLength > 1) { dN += (double)TestNbr[NumberLength-2]/dDosALa31; } if (NumberLength > 2) { dN += (double)TestNbr[NumberLength-3]/dDosALa62; } ExchangeMods(); // TestNbr <- modulus baseExp = groupOrder; powSubGroupOrder = BigInt1; for (indexExp = 0; indexExp < ExponentsGO[indexBase]; indexExp++) { /* PH below comes from Pohlig-Hellman algorithm */ divideExp = BigInt1; j = ExponentsGO[indexBase] - indexExp; do { divideExp = divideExp.multiply(subGroupOrder); basePH = base.modPow(groupOrder.divide(divideExp), mod); j--; } while (j>0 && basePH.equals(BigInt1)); powerPH = power.multiply(base.modPow(runningExp.negate(), mod)). modPow(baseExp.divide(divideExp),mod); baseExp = baseExp.divide(subGroupOrder); if (subGroupOrder.compareTo(BigInteger.valueOf(20L)) < 0) { for (j=subGroupOrder.intValue()-1; j>=0; j--) { if (basePH.modPow(BigInteger.valueOf(j), mod).equals(powerPH)) { break; } } if (j<0) { textExp.setText("No se puede calcular el logaritmo discreto subgrupo="+PrimesGO[indexBase]+" exponente="+indexExp); return; } Exponent = BigInteger.valueOf(j); } else { // Use Pollard's rho method with Brent's modification BigNbrToMont(powerPH, nbrPower); BigNbrToMont(basePH, nbrBase); for (j=0; j<NumberLength; j++) { nbrR2[j] = nbrBase[j]; nbrA2[j] = nbrB2[j] = 0; } nbrB2[0] = 1; addA2 = addB2 = 0; mult2 = 1; brentR = 1; brentK = 0; PollardBrentRho: do { for (j=0; j<NumberLength; j++) { nbrR[j] = nbrR2[j]; nbrA[j] = nbrA2[j]; nbrB[j] = nbrB2[j]; } addA = addA2; addB = addB2; mult1 = mult2; brentR *= 2; do { if (TerminateThread) { return; /* End thread */ } brentK++; magnitude = (nbrR2[mostSignificantDword] << 31) + nbrR2[leastSignificantDword]; if (magnitude < firstLimit) { MontgomeryMult(nbrR2, nbrPower, nbrROther); addA2++; } else { if (magnitude < secondLimit) { MontgomeryMult(nbrR2, nbrR2, nbrROther); mult2 *= 2; addA2 *= 2; addB2 *= 2; } else { MontgomeryMult(nbrR2, nbrBase, nbrROther); addB2++; } } long [] nbrTemp = nbrR2; nbrR2 = nbrROther; nbrROther = nbrTemp; if (mult2 > 10000000) { ExchangeMods(); // TestNbr <- subGroupOrder AdjustExponent(nbrA2, mult2, addA2); AdjustExponent(nbrB2, mult2, addB2); ExchangeMods(); // TestNbr <- modulus mult2 = 1; addA2 = addB2 = 0; } for (j=0; j<NumberLength; j++) { if (nbrR[j] != nbrR2[j]) {break;} } if (j==NumberLength) {break PollardBrentRho;} } while (brentK < brentR); } while (true); ExchangeMods(); // TestNbr <- subGroupOrder AdjustExponent(nbrA, mult1, addA); AdjustExponent(nbrB, mult1, addB); AdjustExponent(nbrA2, mult2, addA2); AdjustExponent(nbrB2, mult2, addB2); SubtractBigNbrModN(nbrA, nbrA2, nbrA); SubtractBigNbrModN(nbrB2, nbrB, nbrB); if (BigNbrIsZero(nbrA)) { for (j=0; j<NumberLength; j++) { nbrK[j] = 0; } bigNbrA2 = BigInt0; bigNbrB2 = BigIntToBigNbr(nbrB); d = subGroupOrder.intValue(); } else { // gcd is 1. ModInvBigNbr(nbrA, nbrB2, TestNbr); MultBigNbrModN(nbrB2, nbrB, nbrK); bigNbrA2 = BigInt1; bigNbrB2 = basePH.modPow(BigIntToBigNbr(nbrK), mod); d = 1; } ExchangeMods(); // TestNbr <- modulus nbrD = BigInteger.valueOf(d); for (j=0; j<d; j++) { if (powerPH.equals(bigNbrB2)) {break;} bigNbrB2 = bigNbrB2.multiply(bigNbrA2).mod(mod); } if (j==d) { textExp.setText("No se puede calcular el logaritmo discreto subgrupo="+PrimesGO[indexBase]+" exponente="+indexExp); return; } Exponent = BigIntToBigNbr(nbrK).add(subGroupOrder.divide(nbrD). multiply(BigInteger.valueOf(j)).mod(subGroupOrder)); } runningExp = runningExp.add(Exponent.multiply(powSubGroupOrder)); powSubGroupOrder = powSubGroupOrder.multiply(subGroupOrder); } nbrV[indexBase] = runningExp; for (indexExp=0; indexExp < indexBase; indexExp++) { nbrV[indexBase] = nbrV[indexBase].subtract(nbrV[indexExp]). multiply(PrimesGO[indexExp].pow(ExponentsGO[indexExp]).modInverse(powSubGroupOrder)); } nbrV[indexBase] = nbrV[indexBase].mod(powSubGroupOrder); logar = logar.add(nbrV[indexBase].multiply(logarMult)); logarMult = logarMult.multiply(powSubGroupOrder); } // Compute group order. BigInteger F = PrimesModBak[index]; BigInteger G = F.subtract(BigInt1); BigInteger P = F; for (indexBase = 0; indexBase < NbrFactors; indexBase++) { for (indexExp = 0; indexExp < ExponentsGO[indexBase]; indexExp++) { if (base.modPow(G.divide(PrimesGO[indexBase]),F).equals(BigInt1)) { G = G.divide(PrimesGO[indexBase]); } } } logar = logar.mod(G); GroupOrder = G; for (i=2; i<ExponentsModBak[index]*2; i*=2) { BigInteger H = F.pow(2); BigInteger K0 = base.modPow(logar, H); BigInteger K1 = base.modPow(logar.add(G), H); BigInteger Num = power.subtract(K0).divide(F).mod(F); BigInteger Den = K1.subtract(K0).divide(F).mod(F); if (Den.equals(BigInt0)) { if (Num.equals(BigInt0)) { textExp.setText("Indeterminación 0/0 -> No se puede calcular el logaritmo discreto"); } else { textExp.setText("No existe el logaritmo discreto"); } return; } logar = logar.add(Num.multiply(Den.modInverse(F)). mod(F).multiply(G)); G = G.multiply(F); F = H; } GroupOrder = GroupOrder. multiply(PrimesMod[index].pow(ExponentsMod[index]-1)); logar = logar.mod(GroupOrder); if (DiscreteLogPeriod.equals(BigInt1)) { DiscreteLogPeriod = GroupOrder; DiscreteLog = logar; } else { BigInteger gcd = GroupOrder.gcd(DiscreteLogPeriod); if (logar.mod(gcd).equals(DiscreteLog.mod(gcd)) == false) { textExp.setText("No existe el logaritmo discreto"); return; } if (GroupOrder.divide(gcd).gcd(gcd).equals(BigInt1) == false) { BigInteger tmp = GroupOrder; GroupOrder = DiscreteLogPeriod; DiscreteLogPeriod = tmp; tmp = logar; logar = DiscreteLog; DiscreteLog = tmp; } GroupOrder = GroupOrder.divide(gcd); logar = logar.mod(GroupOrder); DiscreteLog = (logar.subtract(DiscreteLog)). multiply(DiscreteLogPeriod.modInverse(GroupOrder)). mod(GroupOrder).multiply(DiscreteLogPeriod).add(DiscreteLog); DiscreteLogPeriod = DiscreteLogPeriod.multiply(GroupOrder); } } textExp.setText(DiscreteLog.toString()); textPeriod.setText(DiscreteLogPeriod.toString()); long t=OldTimeElapsed/1000; labelStatus.setText("Tiempo transcurrido: "+ t/86400+"d "+(t%86400)/3600+"h "+((t%3600)/60)+"m "+(t%60)+ "s mult mod: "+lModularMult); } private void BigNbrToMont(BigInteger bigNbr, long [] nbr) { int i; long tmp; int NumberLengthBak = NumberLength; System.arraycopy(TestNbr, 0, CalcAuxGcdU, 0, NLen); BigNbrToBigInt(bigNbr); for (i=NumberLength; i<NumberLengthBak; i++) { TestNbr[i] = 0; } NumberLength = NumberLengthBak; for (i=0; i<NumberLength; i++) { tmp = TestNbr[i]; TestNbr[i] = CalcAuxGcdU[i]; CalcAuxGcdU[i] = tmp; } MultBigNbrModN(CalcAuxGcdU, MontgomeryMultR1, nbr); } // nbr = (nbr * mult + add) % TestNbr private void AdjustExponent(long [] nbr, long mult, long add) { long Pr; int j; Pr = add * DosALa31; for (j=0; j<NumberLength; j++) { Pr = (Pr >>> 31) + mult*nbr[j]; nbr[j] = Pr & 0x7FFFFFFFl; } nbr[j] = (Pr >>> 31); AdjustModN(nbr); } private void ExchangeMods() { long [] Tmp; int Temp; double dTemp; Tmp = TestNbr; TestNbr = TestNbrOther; TestNbrOther = Tmp; Temp = NumberLength; NumberLength = NumberLengthOther; NumberLengthOther = Temp; dTemp = dN; dN = dNOther; dNOther = dTemp; } } // End applet class //