(* -*- Mode: math -*- *) (* :Title: math478.m -- Various number theoretic and encryption algorithms *) (* :Context: *) (* :Summary: The math478.m package defines various number theoretic and encryption algorithms. *) (* :Copyright: 2004 by J. Robert Buchanan *) (* :Package Version: 1.0 *) (* :Mathematica Version: 5.0 *) (* :History: Created - 09/21/2004 by J. Robert Buchanan *) (* :Keywords: cipher, encryption, decryption, codes, number theory *) (* :Sources: _Cryptological Mathematics_ by Robert Edward Lewand, The Mathematical Association of America, 2000, ISBN: 0-88385-719-7 _An Introduction to Cryptography_, by Richard A. Mollin, Chapman & Hall/CRC Press, 2001, ISBN: 1-58488-127-5 *) (* :Warnings: none *) (* :Limitations: none *) (* :Discussion: Implements encryption algorithms for easy-to-break codes. *) (* :Requirements: Mathematica, version 1.0 or higher *) (* :Examples: ShowCipher[enperm = RandomPermutation[26]] Encipher["How's this for security?", enperm] AdditiveCipher[5] MultiplicativeCipher[5] AffineCipher[11, 7] KeywordCipher["mathematics", "f"] LetterFreq["mathematics"] *) (* Set up the package context, including public imports. *) BeginPackage["math478`", {"DiscreteMath`Combinatorica`", "NumberTheory`NumberTheoryFunctions`", "Statistics`DataManipulation`"}] (* Usage message for the exported functions and the context itself. *) inversePermutation::usage = "InversePermutation[perm] takes the permutation of the numbers {0,1,...,25} and produces the inverted permutation." ShowCipher::usage = "ShowCipher[perm] takes the permutation of the numbers {0,1,...,25} and displays the mapping of lowercase letters to their permuted images in uppercase." AdditiveCipher::usage = "AdditiveCipher[key] creates a permutation of the numbers {0,1,...,25} by cyclically shifting them key spaces." MultiplicativeCipher::usage = "MultiplicativeCipher[key] creates a permutation of the numbers {0,1,...,25} by multiplying each number by key and reducing the result modulo 26." AffineCipher::usage = "AffineCipher[mkey, akey] creates a permutation of the numbers {0,1,...,25} by performing an multiplicative cipher with mkey followed by a additive cipher with akey." KeywordCipher::usage = "KeywordCipher[keyword, keyletter] creates a permutation of the numbers {0,1,...,25} by (a) eliminating all duplicates of letters from the keyword, (b) beginning at the position of the key letter, writing the reduced form the keyword, (c) writing the remaining letters of the alphabet in their natural order after the last keyword letter, wrapping around if necessary." Encipher::usage = "Encipher[cleartext, perm] encrypts the plain (human readable) text using the permutation, perm." Decipher::usage = "Decipher[ciphertext, perm] decrypts the encoded cipher text using the permutation, perm." LetterFreq::usage = "LetterFreq[text] counts the relative frequency of the occurence of unique letters in the text string it is given." PHEncipher::usage = "PHEncipher[m,k,p] enciphers plaintext m using the Pohlig-Hellman enciphering function with key k modulo prime p." PHDecipher::usage = "PHDecipher[c,d,p] deciphers ciphertext c using the Pohlig-Hellman deciphering function with key d modulo prime p." PHA::usage = "PHA[alpha,beta,p] solve the discrete logarithm problem, alpha^x === beta (mod p) for x using the Pohlig-Hellman Algorithm." primitiveRoots::usage = "primitiveRoots[p] where p is prime will find all the elements in the range 2, 3, ..., p-1 whose multiplicative order is p-1." RSAValidate::usage = "RSAValidate[{n,k}] will test (within computational feasibility whether {n,k} is a valid public key for the RSA cryptosystem." RSAEncipher::usage = "RSAEncipher[m,{n,k}] produces ciphertext from the from the plaintext m using public key (n,k)." RSADecipher::usage = "RSADecipher[c,{n,d}] produces plaintext from the from the ciphertest c using private key (n,d)." BlumQ::usage = "BlumQ[p] will return True if p is a Blum prime and False otherwise." RabinValidate::usage = "RabinValidate[n] will test (within computational feasibility) whether n is a valid public key for the Rabin cryptosystem." RabinEncipher::usage = "RabinEncipher[m,n] will encipher the plaintext string m, using the public key n." RabinDecipher::usage = "RabinDecipher[c,{p,q}] will decipher the ciphertext list c, using the private key {p,q}." ElGamalValidate::usage = "ElGamalValidate[{p,alpha,ax}] will test (within computational feasibility) whether {p,alpha,ax} is a valid public key for the El Gamal public key cryptosystem." ElGamalEncipher::usage = "ElGamalEncipher[m,{p,alpha,aa}] will encipher the plaintext string m, using the El Gamal enciphering operation with public key {p,alpha,aa}." ElGamalDecipher::usage = "ElGamalDecipher[c,{p,a}] will decipher the ciphertext list c, using the El Gamal deciphering operation with private key {p,a}." SuperIncreasingQ::usage = "SuperIncreasingQ[lis] returns True if lis is a superincreasing sequence and False otherwise." SubsetSum::usage = "SubsetSum[sum,lis] will return a list of 0s and 1s which can be used to express the solution of the Subset Sum Problem." StreamEncipher::usage = "StreamEncipher[m,s] enciphers plaintext m using the Autokey Vignere Cipher with priming key s." StreamDecipher::usage = "StreamDecipher[c,s] deciphers ciphertext c using the Autokey Vignere Cipher with priming key s." LucasLehmer::usage = "LucasLehmer[n] determines if the nth Mersenne number is prime." SolovayStrassen::usage = "SolovayStrassen[n,r] will perform the Solovay-Strassen Probabilistic Primality Test on n with r randomly chosen integers in the interval [2,n-2]. SolovayStrassen[n,{a1,a2,...,ar}] will perform the Solovay-Strassen Probabilistic Primality Test on n using in turn the integers in the list {a1,a2,...,ar}." MillerRabinSelfridge::usage = "MillerRabinSelfridge[n,r] will perform the Miller-Rabin-Selfridge Probabilistic Primality Test on n with r randomly chosen integers in the interval [2,n-2]. MillerRabinSelfridge[n,{a1,a2,...,ar}] will perform the Miller-Rabin-Selfridge Probabilistic Primality Test on n using in turn the integers in the list {a1,a2,...,ar}." ECAdd::usage = "ECAdd[{x1,y1},{x2,y2},a] will perform elliptic curve addition for the elliptic curve defined as y^2 = x^3 + a x + b over the field of real numbers. Either (or both) of the points {xi,yi} can be Infinity." Begin["`Private`"] (* begin the private context (implementation part) *) inversePermutation[perm_List] := Module[{tmp, monosubs}, tmp = Range[0,25]; monosubs = Apply[(#2 -> #1) &, Transpose[{tmp, perm}], 2]; tmp /. monosubs] Encipher[cleartext_String, perm_List] := Module[{tmp, alphabet, monosubs}, alphabet = CharacterRange["a", "z"]; monosubs = Apply[(#1 -> #2) &, Transpose[{alphabet, alphabet[[perm+1]]}], 2]; tmp = Select[Characters[ToLowerCase[cleartext]], LetterQ]; ToUpperCase[tmp /. monosubs]] Decipher[ciphertext_String, perm_List] := Module[{tmp, alphabet, monosubs}, alphabet = CharacterRange["A", "Z"]; monosubs = Apply[(#1 -> #2) &, Transpose[{alphabet, alphabet[[perm]]}], 2]; tmp = Select[Characters[ToUpperCase[ciphertext]], LetterQ]; StringJoin[ToLowerCase[tmp /. monosubs]]] ShowCipher[perm_List] := Module[{alphabet}, alphabet = CharacterRange["a", "z"]; Apply[(#1 \[LongLeftRightArrow] #2) &, Transpose[{alphabet, ToUpperCase[alphabet[[perm+1]]]}], 2]] AdditiveCipher[n_Integer] := Mod[Range[0,25]+ n, 26] MultiplicativeCipher::GCDnot1 = "The greatest common divisor of `1` and `2` is not 1." MultiplicativeCipher[n_Integer] := Module[{gcd}, gcd = GCD[26, n]; If[gcd != 1, Message[MultiplicativeCipher::GCDnot1, n, 26]]; Mod[n Range[0,25], 26]] AffineCipher::GCDnot1 = "The greatest common divisor of `1` and `2` is not 1." AffineCipher[s_Integer, r_Integer] := Module[{gcd}, gcd = GCD[26, s]; If[gcd != 1, Message[AffineCipher::GCDnot1, s, 26]]; Mod[(s Range[0,25]) + r, 26]] KeywordCipher[keyword_String, keyletter_String] := Module[{tmp}, tmp = ToLowerCase[keyword]; tmp = Flatten[ ToCharacterCode[ Map[StringTake[tmp, {#}] &, Union[Take[ Flatten[ Map[StringPosition[tmp, #, 1] &, Union[Characters[tmp]]]], {1, -1, 2}]]]]] - 96; RotateRight[Join[tmp, Complement[Range[26], tmp]], ToCharacterCode[keyletter] - 97]] LetterFreq[txt_String] := Module[{tmp, len}, tmp = Select[Characters[ToUpperCase[txt]], LetterQ]; len = N[Length[tmp]]; tmp = Transpose[Frequencies[tmp]]; Transpose[{Range[Length[tmp[[1]]]], tmp[[1]]/len, tmp[[2]]}]] PHEncipher::GCDnot1 = "The greatest common divisor of `1`-1=`2` and `3` is not 1." PHEncipher[m_?VectorQ, e_Integer, p_?PrimeQ] := Module[{gcd}, gcd=GCD[e,p-1]; If[gcd != 1, Message[PHEncipher::GCDnot1, p,p-1,e], PowerMod[m, e, p] ] ] PHDecipher::GCDnot1 = "The greatest common divisor of `1`-1=`2` and `3` is not 1." PHDecipher[c_?VectorQ, e_Integer, p_?PrimeQ] := Module[{gcd}, gcd=GCD[e,p-1]; If[gcd != 1, Message[PHEncipher::GCDnot1, p,p-1,e], PowerMod[c, e, p] ] ] PHA[alpha_Integer,beta_Integer,p_?PrimeQ]:= Module[{facts,mods},facts=FactorInteger[p-1]; mods=Map[PHAAux[alpha,beta,p,#]&,facts];{Apply[FromDigits, Transpose[{mods,First[Transpose[facts]]}],{1}], Apply[Power,facts,{1}]}] PHAAux[alpha_Integer,beta_Integer,p_?PrimeQ,{pj_?PrimeQ,aj_Integer}]:= Module[{bi,tmp,alphainv},alphainv=PowerMod[alpha,-1,p]; tmp=PowerMod[beta,(p-1)/pj,p]; Do[If[tmp == PowerMod[alpha,i(p-1)/pj,p],bi={i};Break[]],{i,0,pj-1, 1}];Do[tmp=Mod[beta*Power[alphainv,FromDigits[bi,pj]],p]; tmp=PowerMod[tmp,(p-1)/(pj^(k+1)),p]; Do[If[tmp == PowerMod[alpha,i(p-1)/pj,p],bi=Prepend[bi,i]; Break[]],{i,0,pj-1,1}],{k,1,aj-1,1}];bi] primitiveRoots::NoRoots = "Integer `1` has no primitive roots." primitiveRoots[2] = {1} primitiveRoots[4] = {3} primitiveRoots[p_Integer] := Module[{tmp,facts}, tmp = If[EvenQ[p],p/2,p]; facts = FactorInteger[tmp]; If[Or[Length[facts] > 1, 2 == facts[[1,1]]], Message[primitiveRoots::NoRoots,p], primitiveRootsAux[p] ] ] primitiveRootsAux[p_Integer] := Module[{tmp, pos, ords}, tmp = Range[EulerPhi[p]-1]; ords = Map[MultiplicativeOrder[#, p] &, tmp]; pos = Position[ords, EulerPhi[p]]; tmp[[Flatten[pos]]] ] RSAValidate[{n_Integer,k_Integer}] := Module[{facts, exps, tmp, phi}, tmp = FactorInteger[n]; {facts, exps} = Transpose[tmp]; phi = Apply[Times,facts-1]; If[And[2 == Length[tmp], 1 == GCD[k,phi], 2 == Apply[Plus, exps]], {facts,PowerMod[k,-1,phi],StyleForm["Valid",FontColor->RGBColor[0,1,0]]}, StyleForm["Invalid",FontColor->RGBColor[1,0,0]]]] Options[RSAEncipher]={Base -> 26} RSAEncipher[m_String,{n_Integer,k_Integer},opts___Rule]:= Module[{el,base,tmp},base=Base/.{opts}/.Options[RSAEncipher]; el=Floor[Log[base,n]];tmp=ToCharacterCode[m]-65; tmp=PadRight[tmp,Ceiling[Length[tmp]/el]*el,base-1];tmp=Partition[tmp,el]; tmp=Map[FromDigits[#,base]&,tmp];tmp=Map[PowerMod[#,k,n]&,tmp]; tmp=Flatten[65+IntegerDigits[tmp,base,el+1]];FromCharacterCode[tmp]] Options[RSADecipher]={Base -> 26} RSADecipher[c_String,{n_Integer,d_Integer},opts___Rule]:= Module[{el,base,tmp},base=Base/.{opts}/.Options[RSADecipher]; el=Floor[Log[base,n]];tmp=ToCharacterCode[c]-65;tmp=Partition[tmp,el+1]; tmp=Map[FromDigits[#,base]&,tmp];tmp=Map[PowerMod[#,d,n]&,tmp]; tmp=Flatten[65+IntegerDigits[tmp,base,el]];FromCharacterCode[tmp]] RSADecipher[c_List,{n_Integer,d_Integer},opts___Rule]:= Module[{el,base,tmp}, base=Base/.{opts}/.Options[RSADecipher]; el=Floor[Log[base,n]]; tmp=PowerMod[c,d,n]; tmp=Flatten[65+IntegerDigits[tmp,base,el]]; FromCharacterCode[tmp]] BlumQ[p_Integer] := And[PrimeQ[p], 3 == Mod[p, 4]] RabinValidate[n_Integer] := Module[{facts, exps, tmp}, tmp = FactorInteger[n]; {facts, exps} = Transpose[tmp]; If[And[Apply[And, Map[BlumQ, facts]], 2 == Length[tmp], 2 == Apply[Plus, exps]], {facts,StyleForm["Valid",FontColor->RGBColor[0,1,0]]}, StyleForm["Invalid",FontColor->RGBColor[1,0,0]]]] RabinEncipher[m_String, n_Integer] := Module[{tmp}, tmp = ToCharacterCode[m] - 65; tmp = IntegerDigits[tmp, 2, 5]; tmp = Map[Join[#, Take[#, -3]] &, tmp]; tmp = Map[FromDigits[#, 2] &, tmp]; Mod[tmp*tmp, n]] RabinDecipher[c_List, {p_?PrimeQ, q_?PrimeQ}] := Module[{tmp, x, y}, {x, y} = Last[ExtendedGCD[p, q]]; tmp = Transpose[Mod[{ x*p*c^((q + 1)/4) + y*q*c^((p + 1)/4), -x*p*c^((q + 1)/4) + y*q*c^((p + 1)/4), x*p*c^((q + 1)/4) - y*q*c^((p + 1)/4), -x*p*c^((q + 1)/4) - y*q*c^((p + 1)/4)}, p*q]]; tmp = DeleteCases[tmp, _?(# > 201 &), {2}]; tmp = IntegerDigits[tmp, 2, 8]; tmp = DeleteCases[tmp, _?(Take[#, {3, 5}] != Take[#, {6, 8}] &), {2}]; tmp = Map[FromDigits[Take[#,5],2]&,Flatten[tmp,1]]; FromCharacterCode[65 + tmp]] ElGamalValidate[{p_Integer, alpha_Integer, aa_Integer}] := Module[{a, dlog}, dlog = PHA[alpha, aa, p]; a = Apply[ChineseRemainder, dlog]; If[And[PrimeQ[p], 0 < a < p - 1, MemberQ[primitiveRoots[p], alpha], aa == PowerMod[alpha, a, p]], {a,StyleForm["Valid",FontColor->RGBColor[0,1,0]]}, StyleForm["Invalid",FontColor->RGBColor[1,0,0]]]] Options[ElGamalEncipher] = {Base -> 26} Options[ElGamalDecipher] = {Base -> 26} ElGamalEncipher[m_String,{p_Integer,alpha_Integer,aa_Integer},opts___Rule]:= Module[{el,base,tmp,b,beta},base=Base/.{opts}/.Options[ElGamalEncipher]; el=Floor[Log[base,p-1]];tmp=ToCharacterCode[m]-65; tmp=PadRight[tmp,Ceiling[Length[tmp]/el]*el,base-1]; tmp=Partition[tmp,el]; tmp=Map[FromDigits[#,base]&,tmp];b=Random[Integer,{1,p-1}]; beta=PowerMod[alpha,b,p];tmp=Mod[tmp*(aa^b),p]; {beta,tmp}] ElGamalDecipher[{beta_Integer,c_List},{p_Integer,a_Integer},opts___Rule]:= Module[{el,base,tmp,ba},base=Base/.{opts}/.Options[ElGamalDecipher]; ba=PowerMod[beta,p-1-a,p]; el=Floor[Log[base,p-1]];tmp=Mod[ba*c,p]; FromCharacterCode[65+Flatten[IntegerDigits[tmp,base,el]]]] SuperIncreasingQ[x_List] := And[Apply[And, Map[Positive, x]], Apply[And, Map[IntegerQ, x]], Apply[And, Apply[(#1 < #2) &, Transpose[{Drop[x, 1], Drop[FoldList[Plus, 0, x], 2]}], {1}]]] SubsetSum::NotSuperIncreasing = "The sequence `1` is not superincreasing." SubsetSum[d_Integer, x_List] := Module[{sum, result, tmp}, result = Table[0, {Length[x]}]; sum = d; If[SuperIncreasingQ[x], Do[tmp = sum - x[[i]]; If[tmp >= 0, result[[i]] = 1; sum = tmp], {i, Length[x], 1, -1}]; result, Message[SubsetSum::NotSuperIncreasing, x]]] StreamEncipher[m_String, k_String] := Module[{result, p, s, len}, p = ToCharacterCode[m] - 65; s = ToCharacterCode[k] - 65; len = Min[Length[p], Length[s]]; result = Mod[Take[p, len] + Take[s,len], 26] + 65; If[0 == Length[p] - len, FromCharacterCode[result], StringJoin[FromCharacterCode[result], StreamEncipher[StringDrop[m, len], StringTake[m,len]]] ]] StreamDecipher[c_String, k_String] := Module[{result, p, s, len}, p = ToCharacterCode[c] - 65; s = ToCharacterCode[k] - 65; len = Min[Length[p], Length[s]]; result = FromCharacterCode[Mod[Take[p, len] - Take[s,len], 26] + 65]; If[0 == Length[p] - len, result, StringJoin[result,StreamDecipher[StringDrop[c, len], result]] ]] LucasLehmer[n_Integer] := Module[{mn}, mn=(2^n)-1; NestList[Mod[#*#-2,mn]&, 4, n-2] ] Options[SolovayStrassen] = {Verbose -> False} SolovayStrassen[n_?OddQ, r_Integer, opts___Rule] := Module[{a, alist, result, verbosity}, verbosity = Verbose /. {opts} /. Options[SolovayStrassen]; alist = {}; result = "Prime"; Do[a = Random[Integer, {2, n - 2}]; alist = Join[alist, {a}]; If[PowerMod[a, (n - 1)/2, n] != Mod[JacobiSymbol[a, n], n], result = "Composite"; Break[]], {i, r, 1, -1}]; If[verbosity, {result, alist}, result]] /; n > 4 SolovayStrassen[n_?OddQ, alist_List, opts___Rule] := Module[{a, result, verbosity}, verbosity = Verbose /. {opts} /. Options[SolovayStrassen]; result = "Prime"; Do[a = alist[[i]]; If[PowerMod[a, (n - 1)/2, n] != Mod[JacobiSymbol[a, n], n], result = "Composite"; Break[]], {i, 1, Length[alist]}]; If[verbosity, {result, alist}, result]] /; n > 4 Options[MillerRabinSelfridge] = {Verbose -> False} MillerRabinSelfridge::BadN = "The integer `1` cannot be written in the form 1 + m*2^t where m is odd and t is a natural number." MillerRabinSelfridge[n_?OddQ, r_Integer, opts___Rule] := Module[{t, m ,a, alist, result, tmp, verbosity}, verbosity = Verbose /. {opts} /. Options[MillerRabinSelfridge]; t = 0; m = n-1; While[EvenQ[m], m = m/2; t = t+1]; alist = {}; result = "Prime"; Do[ a = Random[Integer, {2, n - 2}]; alist = Join[alist, {a}]; tmp = PowerMod[a, m, n]; If[verbosity, Print["Step 2: ", tmp]]; If[tmp != 1, If[Not[MemberQ[tmp = NestList[Mod[#*#,n]&,tmp,t-1],n-1]], result="Composite"]; If[verbosity, Print["Step 3: ", tmp]]], {i, r, 1, -1}]; If[verbosity, {result, {m, t}, alist}, result]] /; n > 4 MillerRabinSelfridge[n_?OddQ, alist_List, opts___Rule] := Module[{t, m ,a, tmp, result, verbosity}, verbosity = Verbose /. {opts} /. Options[MillerRabinSelfridge]; t = 0; m = n-1; While[EvenQ[m], m = m/2; t = t+1]; result = "Prime"; Do[ a = alist[[i]]; tmp = PowerMod[a, m, n]; If[verbosity, Print["Step 2: ", tmp]]; If[tmp != 1, If[Not[MemberQ[tmp = NestList[Mod[#*#,n]&,tmp,t-1],n-1]], result="Composite"]; If[verbosity, Print["Step 3: ", tmp]]], {i, 1, Length[alist]}]; If[verbosity, {result, {m, t}}, result]] /; n > 4 Options[ECAdd] = {Modulus -> Infinity} ECAdd::BadPt = "The point `1` does not lie on the elliptic curve y^2 == x^3 + `2` x + `3`." ECAdd[{x1_, y1_}, Infinity, {a_,b_}, opts___Rule] := Module[{modulus,validq}, modulus = Modulus /. {opts} /. Options[ECAdd]; validq = If[modulus == Infinity, (y1^2) == (x1^3 + a*x1 + b), 0 == Mod[(y1^2)-(x1^3 + a*x1 + b),modulus]]; If[validq,{x1,y1},Message[ECAdd::Badpt,{x1,y1},a,b]] ] ECAdd[Infinity, {x1_, y1_}, {a_,b_}, opts___Rule] := ECAdd[{x1,y1},Infinity,{a,b},opts] ECAdd[Infinity, Infinity, {a_,b_}, opts___Rule] := Infinity ECAdd[{x1_, y1_}, {x1_, y2_}, {a_,b_}, opts___Rule] := Module[{m,modulus,validq}, modulus = Modulus /. {opts} /. Options[ECAdd]; validq = If[modulus == Infinity, (y1^2) == (x1^3 + a*x1 + b), 0 == Mod[(y1^2)-(x1^3 + a*x1 + b),modulus]]; If[Not[validq],Message[ECAdd::Badpt,{x1,y1},a,b];Return[]]; validq = If[modulus == Infinity, (y2^2) == (x1^3 + a*x1 + b), 0 == Mod[(y2^2)-(x1^3 + a*x1 + b),modulus]]; If[Not[validq],Message[ECAdd::Badpt,{x1,y2},a,b];Return[]]; If[modulus == Infinity, If[And[y1 != y2, (y1+y2) == 0], Return[Infinity]], If[And[y1 != y2, Mod[y1+y2,modulus] == 0], Return[Infinity]]]; If[modulus == Infinity, m = (3 x1*x1 + a)/(2 y1); {m*m - x1 - x1, m (x1 - (m*m - x1 - x1)) - y1}, m = Mod[(3 x1*x1 + a) PowerMod[2 y1,-1,modulus],modulus]; Mod[{m*m - x1 - x1, m (x1 - (m*m - x1 - x1)) - y1},modulus] ] ] ECAdd[{x1_, y1_}, {x2_, y2_}, {a_,b_}, opts___Rule] := Module[{m,modulus,validq}, modulus = Modulus /. {opts} /. Options[ECAdd]; validq = If[modulus == Infinity, (y1^2) == (x1^3 + a*x1 + b), 0 == Mod[(y1^2)-(x1^3 + a*x1 + b),modulus]]; If[Not[validq],Message[ECAdd::Badpt,{x1,y1},a,b];Return[]]; validq = If[modulus == Infinity, (y2^2) == (x2^3 + a*x2 + b), 0 == Mod[(y2^2)-(x2^3 + a*x2 + b),modulus]]; If[Not[validq],Message[ECAdd::Badpt,{x2,y2},a,b];Return[]]; If[modulus == Infinity, m = (y2 - y1)/(x2 - x1); {m*m - x1 - x2, m (x1 - (m*m - x1 - x2)) - y1}, m = Mod[(y2 - y1) PowerMod[x2 - x1,-1,modulus],modulus]; Mod[{m*m - x1 - x2, m (x1 - (m*m - x1 - x2)) - y1},modulus] ] ] End[ ] Protect[ inversePermutation, Encipher, Decipher, ShowCipher, AdditiveCipher, MultiplicativeCipher, AffineCipher, KeywordCipher, LetterFreq ] EndPackage[ ] (* End the package context *) (* * EOF *)