Introduction
To solve cryptographic problems, it is necessary to be able to solve quadratic comparisons of a given module. The algorithm for solving quadratic comparisons is quite simple and does not cause difficulties in solving for small values of the modulus and the free term, however, due to the use of sufficiently large numbers in cryptography, solving quadratic comparisons manually is a very laborious and lengthy process. Of course, to solve quadratic comparisons, you can use the online service. But since the solution of a cryptographic problem does not end with solving a quadratic comparison, it will be convenient for a person involved in cryptography to have a function that can solve quadratic comparisons and freely interact with other functions used by it. That is why it was decided to write a function for solving quadratic comparisons of the form
x ^ 2 ≡ a (mod p) , where
a and
p are mutually prime numbers, in MATLAB.

Immediately, I note that the writing of functions for solving quadratic comparisons was educational in nature; some user functions used in the computation duplicate the functions already existing in the MATLAB environment.
First, I propose to consider the code of two main functions, one of which is designed to solve quadratic comparisons in a composite module, and the second to solve quadratic comparisons modulo a simple module. In this case, we will get acquainted, first of all, with the algorithm for solving quadratic comparisons, and secondly, with the functions that are necessary to perform the calculations themselves.
')
Function to solve complex modulus comparisons
This function allows you to solve quadratic comparisons, both in simple and complex modulus. When calling a function, it is necessary to transfer two variables
a and
p , as a result of its execution, the function will return a vector - a string of two solutions of quadratic comparison opposite in sign.
function [ result ] = sqcomdif( a, p )
The next step in solving a quadratic comparison is to determine the type of the module
p . For this, the custom
factorization function will be used, designed to decompose a number into prime factors. In addition to the string vector with prime factors, the function returns the number of prime factors. In fact, the
factorization function duplicates the standard MATLAB
factor function.
[ mp, sp ] = factorization( p );
After the module has been factorized using the conditional operator, the number of factors is checked. If the number of factors exceeds
1 , that is, the module
p is a composite number, then we need to solve a system of
sp quadratic comparisons (in each of the comparisons the module will be one of the factors of the composite module
p ). Before tackling the obtained system of quadratic comparisons, it is necessary to make sure that each of the quadratic comparisons of this system has a solution. To do this, we use the
for loop, which will make the transition between the elements of the vector with the factors
mp . In the body of the loop, a function will be called that calculates the value of the Legendre symbol for each pair of numbers.
for i=1:1:sp SL( 1, i ) = symvol( a, mp( 1, i ) );
If the value of the Legendre symbol is
1 , then the
count variable will be incremented by
1 . This is necessary so that after all iterations of the cycle have been completed, we can check whether all the equations of the system have a solution, because it depends on whether we will solve the quadratic comparisons of which the system is composed, or we will display messages that the original quadratic comparison does not making.
if SL( 1, i ) == 1 count = count + 1;
If the number of equations of the system is equal to the number of equations that have solutions, then row vectors are created to store intermediate results of the calculation.
if count == sp
Using a
for loop, a transition is made between the multipliers of the
p module. The vector vector
answer1 records the results of solving a quadratic comparison modulo a simple module, which is obtained using the
sqcom function, to which the value of the variable a was passed, as well as the value of the
i-th factor of the module
p . In the vector-line
modul , the quotient of dividing the composite module
p by the
i-th factor of the module
p is written. The
answer2 vector will
contain the result of solving a linear inequality
(p / p (I)) * y ≡ 1 (p (i)) , which will be necessary to calculate the final answer using the formula obtained from the Chinese theorem.
After the cycle has completed its execution, it is necessary to calculate the final answer using the formula:
x = ((p / p (1)) * b (1) * y (1) + ((p / p (2)) * b (2 ) * y (2) + ((p / p (i)) * b (i) * y (i) . To do this, we use element-wise multiplication. As a result, we obtain a row vector, the sum of the elements of which we will find using the command
sum . We will find the remainder of dividing the sum by the composite module
p — this will be one of the solutions of the quadratic comparison modulo. The second answer will be obtained by writing the first answer with the opposite sign.
In the event that initially the module
p turned out to be a prime number, then the solution of a quadratic comparison will be obtained by a one-time function call for solving a quadratic comparison -
sqcom . The second solution will be obtained by taking the first answer with the opposite sign.
else result( 1, 1 ) = sqcom( a, p ); result( 1, 2 ) = - result( 1, 1 ); end
Below is the full sqcomdif function code.
function [ result ] = sqcomdif( a, p ) % % , % . , % , . [ mp, sp ] = factorization( p ); if sp > 1 % count = 0; % % for i=1:1:sp SL( 1, i ) = symvol( a, mp( 1, i ) ); if SL( 1, i ) == 1 count = count + 1; % count 1 end end % if count == sp % answer1 = zeros ( 1, sp ); % modul = zeros ( 1, sp ); % answer2 = zeros ( 1, sp ); % . % for i=1:1:sp answer1( 1, i ) = sqcom( a, mp( 1, i ) ) ; modul( 1, i ) = p / mp( 1, i ); answer2( 1, i ) = lincom ( modul( 1, i ), 1, mp( 1, i ) ); end % result = zeros ( 1, 2 ); result( 1, 1 ) = mod( sum( ( modul .* answer1 ) .* answer2 ), p ); result( 1, 2 ) = - result( 1, 1 ); else result = 'net resheniy'; end else result( 1, 1 ) = sqcom( a, p ); result( 1, 2 ) = - result( 1, 1 ); end end
Function to solve quadratic comparisons modulo simple
This function was repeatedly called in the
sqcomdif function, and as already mentioned, the
sqcom function
is used to solve quadratic comparisons in a simple module and can be called independently of the
sqcomdif function, that is, you can call it and get the right answer without any problems when you are sure that the module is a prime number. Since only deprivation of quadratic comparisons of the form
x ^ 2 ≡ a (mod p) is considered , it is necessary to transfer the numerical values of the variables
a and
p to the function. As a result of the function, one solution of the quadratic comparison will be obtained.
function [ answer ] = sqcom( a, p )
Since the sqcom function can be used separately from the
sqcomdif function, it is necessary to make sure that the number written in the variable
a is a quadratic residue of the module
p . To do this, we use the
symvol function, which allows us to calculate the value of the Legendre symbol for a specified pair of numbers.
[ Symvol_Lejandra ] = symvol( a, p );
If the value of the
Symvol_Lejandra variable is
1 , then a is a quadratic residue modulo
p and further steps will be taken to find a solution to the quadratic comparison. We need to write the number
(p - 1) in the form
2 ^ r * q . The initial values of the variables
r ,
q are given on the basis that
(p - 1) is an odd number. However, if this is not the case, they will change during the execution of the cycle (until
q becomes an odd number).
q = p - 1; r = 0; otn = q / 2; while ( ( q - floor( otn ) * 2 ) == 0 ) q = otn; r = r + 1; otn = q / 2; end
Now it is necessary to find the value of the variable
b , which equals
b = a ^ q (mod p) . Since
a and
q are usually expressed by sufficiently large numbers, it will not be possible to raise the number
a to the power of
q in the usual way, since in most cases an overflow will occur. Therefore, the exponentiation must be performed according to the quadriration method. In order to accomplish this, it is necessary to call the
kvadrirovanie function and transfer to it the value of the base, the exponent, and the module on which the calculations will be performed.
b = kvadrirovanie( a, q, p );
To continue the calculations, you need to find the smallest non-negative number
f , which will be a quadratic non-residue modulo
p . For this, the variable
f is assigned the value
1 , and using the
symvol function, the value of the Legendre symbol is determined for a pair of numbers
f and
p . If the Legendre symbol for
1 and
p is
1 , then the variable
f , using the
while loop , increases until a value that is a quadratic non-residue modulo
p is reached.
f = 1; sym_lej = symvol( f, p ); while sym_lej ~= -1 f = f + 1; sym_lej = symvol( f, p ); end
Now the value of
f , which is a quadratic non-residue modulo
p , must be raised to the power of
q . For this, it is also necessary to resort to the
kvadrirovanie function, the variable
k must be assigned the value
0 .
g = kvadrirovanie( f, q, p); k = 0;
After the above actions, you need to check the value of the variable
b . If
b is comparable with
1 modulo
p , then it is necessary to proceed to the calculation of the answer, otherwise find the smallest non-negative number
m such that
b ^ (2 ^ m) 1 (mod p) . When such a value of
m is found, it is necessary to recalculate the values of the variables
k ,
g ,
b , after which the variable
r is assigned the value of
m . But this is not all, it is necessary to check that the new value of the variable
b is also comparable to
1 modulo
p . If this is not the case, then it is necessary to return to the selection of the number
m . The variable
pok is needed to avoid performing certain mathematical actions twice.
if b ~= 1 while b ~= 1 m = 0; b1 = kvadrirovanie( b, 2^m, p ); while mod( b1, p) ~= 1 m = m + 1; b1 = kvadrirovanie( b, 2^m, p ); end pok = 2^(rm); g1 = kvadrirovanie( g, pok, p ); b = mod( ( b*g1), p ); k = fix(k + pok); r = m; end end
After
m has been found that satisfies the above conditions, we can proceed to the direct calculation of the answer. The answer is calculated by the formula
x = a ^ ((q + 1) / 2) * g ^ (k / 2) (mod p) . To calculate both factors, we use the quadration function; we take the result modulo
p .
first = kvadrirovanie( a, ( ( q + 1 ) / 2 ), p ); second = kvadrirovanie( g, ( k / 2 ), p ); answer = mod( ( first * second ), p);
The result obtained cannot always be written optimally, modulo
p . Therefore it is necessary to perform the following check:
delta = p - answer; if delta < answer answer = delta; end
The following is the full
sqcom function
code :
function [ answer ] = sqcom( a, p ) % % % x^2 = a ( mod p ) , % . % . a=mod(a,p); % 1 [ Symvol_Lejandra ] = symvol( a, p ); if Symvol_Lejandra == 1 % 2 q, r, b q = p - 1; r = 0; otn = q / 2; while ( ( q - floor( otn ) * 2 ) == 0 ) q = otn; r = r + 1; otn = q / 2; end b = kvadrirovanie( a, q, p ); % 3 f f = 1; sym_lej = symvol( f, p ); while sym_lej ~= -1 f = f + 1; sym_lej = symvol( f, p ); end g = kvadrirovanie( f, q, p); k = 0; % 4 if b ~= 1 while b ~= 1 m = 0; b1 = kvadrirovanie( b, 2^m, p ); while mod( b1, p) ~= 1 m = m + 1; b1 = kvadrirovanie( b, 2^m, p ); end pok = 2^(rm); g1 = kvadrirovanie( g, pok, p ); b = mod( ( b*g1), p ); k = fix(k + pok); r = m; end end % 5 first = kvadrirovanie( a, ( ( q + 1 ) / 2 ), p ); second = kvadrirovanie( g, ( k / 2 ), p ); answer = mod( ( first * second ), p); delta = p - answer; if delta < answer answer = delta; end else answer = 'net resheniya'; end
And now I propose to become familiar with the auxiliary functions that are used both in solving quadratic comparisons, and so can be used separately.

Factor expansion
When solving a quadratic comparison, in many cases it is necessary to resort to factorization of the number, and this operation will also have to be resorted to when it is necessary to check: is the number simple or composite.
The
factorization function is passed a number that needs to be factorized. As a result, the function returns a vector - a string with multipliers and the number of these multipliers.
function [ mnojitel, ind ] = factorization( delimoe )
A function consists of a
switch statement that performs various actions depending on the value of the input variable
delimoe . So if
delimoe = 1 , then
mnojitel is written
1 to the vector storing the result of the factorization, and the variable
ind , which stores the number of factors, is also written
1 . Similar actions are performed in the event that
delimoe is
-1 .
switch delimoe case { 1 } mnojitel( 1, 1 )=1; ind=1; case { -1 } mnojitel( 1, 1 )= -1; ind = 1;
If these conditions are not met, then we check the sign of the number factorized. If
delimoe is less than
0 , then
-1 is written to the first factor,
2 is written to
ind , and the function will continue to work with the module of the
delimoe variable
. If the number is greater than
0 , then the value of
ind is assigned to
1 .
otherwise if delimoe < 0 mnojitel( 1, 1 )= -1; ind = 2; delimoe = abs ( delimoe ); else ind = 1; end
The
while loop runs as long as
delimoe is not equal to
delitel , with the initial
delitel variable
being assigned the value
2 . At each iteration of the loop, the remainder of
delimoe divided by
delitel is written into the
ostatok variable . If the remainder is
0 , that is, the
delitel is a
delimoe multiplier, then this value is written to the vector in which the multipliers are stored, and the counter of this vector is increased by
1 . In this case, the
delimoe variable
is assigned the quotient of the division performed. If the remainder of the division is not equal to
0 , then the
delitel is increased by
1 . When the exit from the loop occurs, the value remaining in the
delimoe variable is written to the vector with factors as one of the factors.
while ( delimoe ~= delitel ) ostatok = mod( delimoe, delitel ); if ostatok ~= 0 delitel = delitel + 1; else delimoe = delimoe / delitel; mnojitel( 1, ind ) = delitel; ind = ind + 1; end end mnojitel( 1, ind ) = delimoe;
Below is the full code of the
factorization function.
function [ mnojitel, ind ] = factorization( delimoe ) % % delitel = 2; % switch delimoe case { 1 } mnojitel( 1, 1 )=1; ind=1; case { -1 } mnojitel( 1, 1 )= -1; ind = 1; otherwise if delimoe < 0 mnojitel( 1, 1 )= -1; ind = 2; delimoe = abs ( delimoe ); else ind = 1; end while ( delimoe ~= delitel ) ostatok = mod( delimoe, delitel ); if ostatok ~= 0 delitel = delitel + 1; else delimoe = delimoe / delitel; mnojitel( 1, ind ) = delitel; ind = ind + 1; end end mnojitel( 1, ind ) = delimoe; end end
Calculating the value of the Legendre symbol
In order to check whether a number is a quadratic residue by a given module (in this case, a quadratic comparison has a solution) or a quadratic non-residue (then such a quadratic comparison has no solution), the Legendre symbol is used, which is denoted in the Russian literature as
L (a; p) , in foreign literature as
L (a / p) .
The Legendre symbol can take the following values:
L (a; p) = 1 , in this case a belongs to QR, the quadratic comparison has the solution
L (a; p) = -1 , in which case a belongs to QNR, the quadratic comparison has no solution
If
L (a; p) = 0 , then a and p are not mutually simple numbers, that is, the
gcd (a; p) is not equal to
1The following properties are used to calculate the value of the Legendre symbol:
- L (1; p) = 1
- L (-1; p) = (-1) ^ ((p - 1) / 2)
- L (2; p) = (-1) ^ ((p ^ 2 - 1) / 8)
- If ≡ b * (mod p) , then L (b * ; p) = L (b; p) * L (; p)
- If ≡ b (mod p) , then L (a; p) = L (b; p)
- If a and p are prime numbers, then L (a; p) = (-1) ^ (((p - 1) * (a-1)) / 4) * L (p; a) . The latter property is called the Gauss law of reciprocity.
The calculation of the Legendre symbol is performed on the basis of the above properties. As soon as one of the conditions is fulfilled, we begin to check the properties for the resulting pair
a and
p , until the final value of the Legendre symbol is found.
Now consider how you can implement the calculation of the value of the Legendre symbol programmatically.
The function will return the value of the Legendre symbol for a pair of numbers
a and
p that were passed to it. This is evident from the function header:
function [ sl ] = symvol( a, p )
The next step is essentially the use of property
5 . If the number
a is greater than the modulus
p , then it can be replaced by a smaller number comparable to a modulo
p .
a=mod( a, p );
The resulting number is, trying to factor out. For factoring a factor, a custom function
factorization was written that returns a vector with prime factors that make up the number
a , as well as their number. This feature has been described in detail above.
[ mnoj, ind ] = factorization( a );
If
a was not a prime number, then we will act by property
4 . That is, the value of the Legendre symbol for a pair of numbers
L (a; p) , we find as the product of the values of the Legendre symbols for numbers that are prime factors of
a . To store intermediate results, create a row vector filled with zeros with a dimension equal to the number of factors of
a . If
a is simple, then this string will consist of one element.
sl = zeros( 1, ind );
To calculate the Legendre symbol for each multiplier, alternately use the
for loop, which will change its values from
1 to the number of the last multiplier. In the body of this cycle, the immediate process of calculating the value of the Legendre symbol takes place using the properties listed above.
The code for checking property 1 is as follows:
Since when checking the value of a symbol when it is
L (-1, p) , by property
2 , it is necessary to calculate the value
(-1) ^ ((p - 1) / 2) , it is necessary to use another conditional operator, in which be checked if the indicator
-1 is an even or odd number. Depending on this, the value of the Legendre symbol will vary. If the index is even, then the Legendre symbol will be equal to
1 , otherwise
-1 . The use of this conditional operator avoids the erection of
-1 to the power of
(p - 1) / 2 , which is a very costly operation.
Similar actions are carried out in the case when it is necessary to calculate the value of the Legendre symbol in its form
L (2; p) . In this case, it will be
(-1) ^ ((p ^ 2 - 1) / 8) .
After checking this condition, the number
a is passed to the function in order to check whether it is simple or not. If the number
a is a compound (the number of its factors
ind1 is greater than
1 ), then recursion occurs and the number
a is transferred to the same function to perform further calculations.
[ mn, ind1 ] = factorization( mnoj( 1, i ) );
Otherwise, the number
a is prime. If at the same time it is not equal to
-1 ,
1 ,
2 , then one has to use property
6 , the Gauss reciprocity law. The sign in front of the Legendre symbol is determined by determining the parity of the factors of its index. He turns in plus if at least one of the factors is even. After that, a recursive call to the
symvol function
occurs , and the arguments are passed to it in a different order.
elseif and( mnoj(1,i)~=-1, and( mnoj( 1, i ) ~= 1, mnoj( 1, i ) ~= 2 ) )
As a result of the verification of the above conditions, all possible variants of the number a were covered.
If initially the number a was composite, then the variable sl is written the product of all elements of the string - the vector sl , after which the resulting value is returned to the program that caused this function. if ind ~= 1 sl = prod( sl );
The complete code for the symvol function : function [ sl ] = symvol( a, p ) % L(a,p) % , % , a=mod( a, p ); % [ mnoj, ind ] = factorization( a ); % sl = zeros( 1, ind ); % % for i = 1:ind % L(1,p) if mnoj( 1, i ) == 1 sl( 1, i ) = 1; end % L(-1,p) if mnoj( 1, i ) == -1 if mod( ( ( p - 1 ) ) / 2, 2 ) == 0 sl( 1, i ) = 1; else sl( 1, i ) = -1; end end % L(2,p) if mnoj( 1, i ) == 2 % 1, -1 if mod( ( ( p^2 - 1 ) / 8 ), 2 ) == 0 sl(1,i)=1; else sl(1,i)=-1; end end [ mn, ind1 ] = factorization( mnoj( 1, i ) ); % , % if ind1 > 1 % - sl( 1, i ) = symvol( mnoj(1,i), p ); % , elseif and( mnoj(1,i)~=-1, and( mnoj( 1, i ) ~= 1, mnoj( 1, i ) ~= 2 ) )% - , 1 2 if or( mod( ( ( p - 1 ) / 2 ), 2 ) == 0, mod( ( ( mnoj( 1, i ) - 1 ) / 2 ), 2 ) == 0 ) % - sl(1,i)= symvol( p, mnoj( 1, i ) ); % L(p,a) else sl(1,i)=-symvol( p, mnoj( 1, i ) ); % -L(p,a) end end end if ind ~= 1 sl = prod( sl ); % L(a,p) end end
Raising a number to a power by quadrature method
When solving a quadratic comparison, one often comes across the need to build large numbers to a power with an exponent equal to several hundred, or even thousands. With the usual raising of a number to such a degree, it is rather easy to come across such an unpleasant phenomenon as overflow. In order to avoid it, it is necessary to raise such numbers to a power using the quadration method.Consider the algorithm of this method:- .
- , 1 m=a .
- :
- Go to the next bit on the right and repeat the steps described in paragraph 3, until the calculations for the last bit are performed.
And now let's look at how the kvadrirovanie function works . The values of the base, indicator and module are transferred to the function, and the function returns a single number - the final result. function [ result ] = kvadrirovanie( a, q, p )
We translate the exponent q into a binary view, and since the result will consist of individual characters, we use the size command to determine the number of bits needed to record the exponent. q = dec2bin( q ); size_q = size(q);
, :
m uint64 .
for ,
i 2 1 ,
q , , .
if size_q( 1, 2 ) >= 2 m = uint64(a); for i=2:1:size_q(1,2)
, ,
i- , ,
m . ,
1 ,
m^2 ,
,
.
if q(1,i)=='1' m = uint64( mod( ( mod( ( m^2 ), p ) * a ), p ));
,
q(1,i)=='0' ,
m^2 .
else m = uint64(mod( ( m^2 ), p )); end
,
m result .
result =uint64(m);
In the exponent in binary form is equal to 1 , then the initial number itself is written into the result variable , which was required to be raised to a power. elseif q(1,1) == '1' result = uint64( a );
If this condition is not fulfilled, then the exponent is 0 . In this case, the result variable will be written 1 . else result = 1; end
Full function code for exponentiation by the quadration method: function [ result ] = kvadrirovanie( a, q, p ) % % , 1 % , % . . q = dec2bin( q ); size_q = size(q); if size_q( 1, 2 ) >= 2 m = uint64(a); for i=2:1:size_q(1,2) if q(1,i)=='1' m = uint64( mod( ( mod( ( m^2 ), p ) * a ), p )); else m = uint64(mod( ( m^2 ), p )); end end result =uint64(m); elseif q(1,1) == '1' result = uint64( a); else result = 1; end end
Linear comparison solution
When solving quadratic comparisons in complex moduli, the formula obtained from the Chinese theorem will be applied. In order to calculate the answer using this formula, it becomes necessary to solve the linear comparison k * x ≡ b (mod p) . This function is intended to solve them with the help of an inverse element, that is, such a number k1 which, when multiplied by k , will give a result comparable with 1 given a modulus p .The lincom function passes the coefficient values for an unknown k , the number b with which the left side is comparable, and also the module p , and the function returns respectively a linear comparison solution with the specified parameters. function [ x ] = lincom ( k, b, p)
, . . , , .
( a, b ) , –
b ,
k .
b0 b1 , .
p , which was written to the variable pr , is divided completely into the coefficient k , then the inverse element will be equal to 1 . Otherwise, b1 will be calculated using a while loop . The cycle will recalculate the values of the variable b0 . After a new value of b0 is found , the swap function will exchange the values between the variables b0 and b1 . Also, with each iteration of the cycle, new values will be assigned to a number of other variables. b0=0; b1=1;
After the cycle ends, it will be necessary to calculate the value of the linear comparison solution. It will be equal to the product of the variables b1 and b , taken modulo p (for this operation at the beginning of the function the modulus value was duplicated using the variable pr ). x = mod( b1*b, p );
The following is the entire function code: function [ x ] = lincom ( k, b, p) % k*x=b ( mod p ) pr=p; b0=0; b1=1; % ostatok = mod( pr, k ); while ostatok~=0 chastnoe = floor( pr / k ); b0 = b0 - b1 * chastnoe; [ b0, b1 ] = swap( b0, b1 ); pr = k; k = ostatok; ostatok = mod( pr, k ); end x = mod( b1*b, p ); end
The list of sources that helped in creating features and writing an article:
- Summaries and lectures Plotnikova L.I. (who in the subject will understand)
- http://math.hashcode.ru
- http://mathhelpplanet.com
- http://www.wolframalpha.com