📚 The CoCalc Library - books, templates and other resources
License: OTHER
Math 157: Intro to Mathematical Software
UC San Diego, winter 2018
February 12, 2018: Number theory and cryptography (part 1 of 3)
Administrivia:
Homework 5 is now posted.
Schedule change for this week: due to a schedule conflict, my office hours will take place Thursday 3-4 instead of 4-5.
The material we will be discussing this week is treated in far more detail in Math 187B (Mathematics of Modern Cryptography). Both 187B and its prerequisite Math 187A (Introduction to Cryptography) use CoCalc and Sage.
Leftover from graph theory: minimum spanning trees
Since this week's problem set includes an example of a minimum spanning tree calculation, let me go over this once more in detail.
Suppose (as in this case) that is connected. A spanning tree is a subgraph of (i.e., the same vertices but possibly fewer edges) which is itself a tree, that is, it is connected with no loops.
Let's see that this is indeed a tree by plotting it separately:
Let's now plot it superimposed on top of the original graph:
There is a famous formula for counting the spanning trees in a graph. This involves some linear algebra on the adjacency matrix!
To talk about minimum spanning trees, we must assign a weight to each edge of the graph; the weight of a spanning tree is then the sum of the weights of the edges that are used in the tree, and the goal is to minimize this. This models the problem of finding a smaller network that still connects everything together while minimizing some measure of cost.
The minimum spanning tree function takes as an input a function to compute the edge of each weight. In some cases, it is natural to determine these weights in advance and use them as edge labels; but this is not obligatory.
On the problem set, you will be doing a similar calculation, except that the graph will be an example constructed from some real-life data. You will thus be using pandas to get the data into Sage.
And now for something completely different...
Some number theory for cryptography
It is very very easy to compute the greatest common divisor of two integers, even very large ones, because of the Euclidean algorithm.
The same technique can be used to find, given two integers and , two other integers and such that . This is sometimes called the extended Euclidean algorithm, which helps explain the notation in Sage.
Oh, hmm, what was that command called again?
These values can be used to make explicit the Chinese remainder theorem: if , then every system of equations of the form has a solution (which is unique up to adding a mulitple of ).
By contrast, finding the prime factorization of a positive integer can be difficult. Not always, though.
What makes the last example particularly easy is that you can test for the very small prime factors quickly (trial division), and then it turns out to be much easier to tell whether or not a big number is prime than to actually factor it. There are other features that make some numbers easier to factor than others, but more on this later.
A reminder about modular exponentiation
One reason for this is that it is easy to do modular exponentiation, i.e., the remainder of a^b upon division by c. We have seen this story once before, so this will be a very quick recap.
How not to do this:
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-116-4823e954010f> in <module>()
----> 1 (Integer(2)**m) % n ## No dice
/ext/sage/sage-8.1/src/sage/rings/integer.pyx in sage.rings.integer.Integer.__pow__ (build/cythonized/sage/rings/integer.c:14183)()
2058 elif mpz_cmp_si(_self.value, -1) == 0:
2059 return self if n % 2 else -self
-> 2060 raise RuntimeError("exponent must be at most %s" % sys.maxsize)
2061
2062 if nn == 0:
RuntimeError: exponent must be at most 9223372036854775807
What does work:
Remember that even working mod , directly computing with factors is not feasible. These computations all use repeated squaring.
As I mentioned some time ago, Sage uses modular exponentation as a screening tool for primality by expoliting the little Fermat theorem: if is a prime number, then for any positive integer , is divisible by . (In the language of congruences, .)
One possible proof (for the case , but this can be extended to a general argument): in the binomial expansion all of the intermediate coefficients are divisible by (so plugging in gives the claim). For example...
The contrapositive of the little Fermat theorem is: if is not divisible by for some , then is not prime. This test works extremely well!
Again, we have seen that this test is not foolproof; it can return false positives for primality (e.g., Carmichael numbers).
Consequently, this method cannot be used to certify that a particular number is prime, only that it is not prime. There are efficient algorithms that can certify primality, but they are somewhat harder to describe. One important one is the Agrawal-Kayal-Saxena (AKS) algorithm, which is both extremely simple and polynomial-time (though not as efficient in practice as some other methods). Kayal and Saxena were undergraduates at the time they discovered it (2002)!
One way to say this is that "prime numbers are easy to factor". The reason this is not an empty statement is because you have to know when to stop factoring!
In any case, the Fermat test is so effective that any number that passes it is probably prime, and this is sometimes good enough (though not for cryptography!).
---------------------------------------------------------------------------
KeyboardInterrupt Traceback (most recent call last)
<ipython-input-128-551a245320ef> in <module>()
1 for n in range(Integer(10)**Integer(4)):
2 m = Integer(10)**Integer(500) + n
----> 3 if mod(Integer(2),m)**m == Integer(2) and not is_prime(m): print m
4 print("no more examples found")
/ext/sage/sage-8.1/local/lib/python2.7/site-packages/sage/arith/misc.pyc in is_prime(n)
473 """
474 try:
--> 475 return n.is_prime()
476 except (AttributeError, NotImplementedError):
477 return ZZ(n).is_prime()
/ext/sage/sage-8.1/src/sage/rings/integer.pyx in sage.rings.integer.Integer.is_prime (build/cythonized/sage/rings/integer.c:32514)()
4930 proof = get_flag(proof, "arithmetic")
4931 if proof:
-> 4932 return self.__pari__().isprime()
4933 else:
4934 return self.__pari__().ispseudoprime()
cypari2/gen.pyx in cypari2.gen.Gen.isprime()
src/cysignals/signals.pyx in cysignals.signals.sig_raise_exception()
KeyboardInterrupt:
The fact that prime numbers are (relatively) easy to identify makes it possible to have functions like this:
Multiplicative order and primitive roots
Let be integers with . Using the extended Euclidean algorithm as above, one can find a multiplicative inverse of modulo , i.e., a value such that . In fact, Sage will do this automatically.
As a consequence of the existence of the multiplicative inverse, there always exists a positive integer such that . (There must be two powers that coincide mod , and we can cancel the powers of from one side.)
If is prime, the little Fermat theorem implies that always works (although it need not be the smallest such value; more on this in a moment). For general , there is a generalization of the Fermat theorem due to Euler: we have where denotes the Euler phi function (or totient function): if has prime factorization , then For those who know group theory: recall that this works because the residue classes mod which have no common factor with form a group under multiplication, and is its order.
So now it makes sense to consider the smallest positive integer such that . This integer must divide , otherwise the remainder of mod would be an even smaller value. (Or use group theory!) This is called the multiplicative order of mod .
Important result: if is prime, then there is always at least one value of for which the multiplicative order of mod is equal to the maximum possible value . Any such is called a primitive root mod . These play an important role in the use of discrete logarithms in cryptography.
(Abstract algebra interpretation; if is prime, then is a cyclic group!)