Navigation

This tutorial uses Sage to study elementary number theory and the RSA
public key cryptosystem. A number of Sage commands will be presented
that help us to perform basic number theoretic operations such as
greatest common divisor and Euler’s phi function. We then present the
RSA cryptosystem and use Sage’s built-in commands to encrypt and
decrypt data via the RSA algorithm. Note that this tutorial on RSA is
for pedagogy purposes only. For further details on cryptography or
the security of various cryptosystems, consult specialized texts such
as
[MenezesEtAl1996],
[Stinson2006], and
[TrappeWashington2006].

We first review basic concepts from elementary number theory,
including the notion of primes, greatest common divisors, congruences
and Euler’s phi function. The number theoretic concepts and Sage
commands introduced will be referred to in later sections when we
present the RSA algorithm.

Public key cryptography uses many fundamental concepts from number
theory, such as prime numbers and greatest common divisors. A
positive integer \(n > 1\) is said to be prime if its factors are
exclusively 1 and itself. In Sage, we can obtain the first 20 prime
numbers using the command primes_first_n:

Let \(a\) and \(b\) be integers, not both zero. Then the greatest common
divisor (GCD) of \(a\) and \(b\) is the largest positive integer which is
a factor of both \(a\) and \(b\). We use \(\gcd(a,b)\) to denote this
largest positive factor. One can extend this definition by setting
\(\gcd(0,0) = 0\). Sage uses gcd(a,b) to denote the GCD of \(a\)
and \(b\). The GCD of any two distinct primes is 1, and the GCD of 18
and 27 is 9.

sage: gcd(3,59)1sage: gcd(18,27)9

If \(\gcd(a,b) = 1\), we say that \(a\) is coprime (or relatively
prime) to \(b\). In particular, \(\gcd(3, 59) = 1\) so 3 is coprime to 59
and vice versa.

When one integer is divided by a non-zero integer, we usually get a
remainder. For example, upon dividing 23 by 5, we get a remainder of
3; when 8 is divided by 5, the remainder is again 3. The notion of
congruence helps us to describe the situation in which two integers
have the same remainder upon division by a non-zero integer. Let
\(a,b,n \in \ZZ\) such that \(n \neq 0\). If \(a\) and \(b\) have the
same remainder upon division by \(n\), then we say that \(a\) is
congruent to \(b\) modulo \(n\) and denote this relationship by

\[a \equiv b \pmod{n}\]

This definition is equivalent to saying that \(n\) divides the
difference of \(a\) and \(b\), i.e. \(n \;|\; (a - b)\). Thus
\(23 \equiv 8 \pmod{5}\) because when both 23 and 8 are divided by 5, we
end up with a remainder of 3. The command mod allows us to
compute such a remainder:

Consider all the integers from 1 to 20, inclusive. List all those
integers that are coprime to 20. In other words, we want to find
those integers \(n\), where \(1 \leq n \leq 20\), such that
\(\gcd(n,20) = 1\). The latter task can be easily accomplished with a
little bit of Sage programming:

The above programming statements can be saved to a text file called,
say, /home/mvngu/totient.sage, organizing it as follows to enhance
readability.

L=[]forninrange(1,21):ifgcd(n,20)==1:L.append(n)L

We refer to totient.sage as a Sage script, just as one would refer
to a file containing Python code as a Python script. We use 4 space
indentations, which is a coding convention in Sage as well as Python
programming, instead of tabs.

The command load can be used to read the file containing our
programming statements into Sage and, upon loading the content of the
file, have Sage execute those statements:

load("/home/mvngu/totient.sage")[1, 3, 7, 9, 11, 13, 17, 19]

From the latter list, there are 8 integers in the closed interval
\([1, 20]\) that are coprime to 20. Without explicitly generating the
list

1 3 7 9 11 13 17 19

how can we compute the number of integers in \([1, 20]\) that are
coprime to 20? This is where Euler’s phi function comes in handy.
Let \(n \in \ZZ\) be positive. Then Euler’s phi function counts the
number of integers \(a\), with \(1 \leq a \leq n\), such that
\(\gcd(a,n) = 1\). This number is denoted by \(\varphi(n)\). Euler’s phi
function is sometimes referred to as Euler’s totient function, hence
the name totient.sage for the above Sage script. The command
euler_phi implements Euler’s phi function. To compute
\(\varphi(20)\) without explicitly generating the above list, we proceed
as follows:

Cryptography is the science (some might say art) of concealing
data. Imagine that we are composing a confidential email to
someone. Having written the email, we can send it in one of two ways.
The first, and usually convenient, way is to simply press the send
button and not care about how our email will be delivered. Sending an
email in this manner is similar to writing our confidential message on
a postcard and post it without enclosing our postcard inside an
envelope. Anyone who can access our postcard can see our message.
On the other hand, before sending our email, we can scramble the
confidential message and then press the send button. Scrambling our
message is similar to enclosing our postcard inside an envelope.
While not 100% secure, at least we know that anyone wanting to read
our postcard has to open the envelope.

In cryptography parlance, our message is called plaintext. The
process of scrambling our message is referred to as encryption.
After encrypting our message, the scrambled version is called
ciphertext. From the ciphertext, we can recover our original
unscrambled message via decryption. The following figure
illustrates the processes of encryption and decryption. A
cryptosystem is comprised of a pair of related encryption and
decryption processes.

be the set of capital letters of the English alphabet. Furthermore,
let

\[\Phi
=
\{ 65, 66, 67, \dots, 90 \}\]

be the American Standard Code for Information Interchange (ASCII)
encodings of the upper case English letters. Then the above table
explicitly describes the mapping \(f: \Sigma \longrightarrow \Phi\).
(For those familiar with ASCII, \(f\) is actually a common process for
encoding elements of \(\Sigma\), rather than a cryptographic
“scrambling” process per se.) To scramble a message written using
the alphabet \(\Sigma\), we simply replace each capital letter of the
message with its corresponding ASCII encoding. However, the
scrambling process described in the above table provides,
cryptographically speaking, very little to no security at all and we
strongly discourage its use in practice.

The Rivest, Shamir, Adleman (RSA) cryptosystem is an example of a
public key cryptosystem. RSA uses a public key to
encrypt messages and decryption is performed using a corresponding
private key. We can distribute our public keys, but for
security reasons we should keep our private keys to ourselves. The
encryption and decryption processes draw upon techniques from
elementary number theory. The algorithm below is adapted from page
165 of [TrappeWashington2006]. It outlines the RSA procedure for
encryption and decryption.

Positive integers of the form \(M_m = 2^m - 1\) are called
Mersenne numbers. If \(p\) is prime and \(M_p = 2^p - 1\) is also
prime, then \(M_p\) is called a Mersenne prime. For example, 31
is prime and \(M_{31} = 2^{31} - 1\) is a Mersenne prime, as can be
verified using the command is_prime(p). This command returns
True if its argument p is precisely a prime number;
otherwise it returns False. By definition, a prime must be a
positive integer, hence is_prime(-2) returns False
although we know that 2 is prime. Indeed, the number
\(M_{61} = 2^{61} - 1\) is also a Mersenne prime. We can use
\(M_{31}\) and \(M_{61}\) to work through step 1 in the RSA algorithm:

A word of warning is in order here. In the above code example, the
choice of \(p\) and \(q\) as Mersenne primes, and with so many digits far
apart from each other, is a very bad choice in terms of cryptographic
security. However, we shall use the above chosen numeric values for
\(p\) and \(q\) for the remainder of this tutorial, always bearing in mind
that they have been chosen for pedagogy purposes only. Refer to
[MenezesEtAl1996],
[Stinson2006], and
[TrappeWashington2006]
for in-depth discussions on the security of RSA, or consult other
specialized texts.

For step 2, we need to find a positive integer that is coprime to
\(\varphi(n)\). The set of integers is implemented within the Sage
module sage.rings.integer_ring. Various operations on
integers can be accessed via the ZZ.* family of functions.
For instance, the command ZZ.random_element(n) returns a
pseudo-random integer uniformly distributed within the closed interval
\([0, n-1]\).

We can compute the value \(\varphi(n)\) by calling the sage function
euler_phi(n), but for arbitrarily large prime numbers \(p\) and \(q\),
this can take an enormous amount of time. Indeed, the private key
can be quickly deduced from the public key once you know \(\varphi(n)\),
so it is an important part of the security of the RSA cryptosystem that
\(\varphi(n)\) cannot be computed in a short time, if only \(n\) is known.
On the other hand, if the private key is available, we can compute
\(\varphi(n)=(p-1)(q-1)\) in a very short time.

Using a simple programming loop, we can compute the
required value of \(e\) as follows:

As e is a pseudo-random integer, its numeric value changes
after each execution of e=ZZ.random_element(phi).

To calculate a value for d in step 3 of the RSA algorithm, we use
the extended Euclidean algorithm. By definition of congruence,
\(de \equiv 1 \pmod{\varphi(n)}\) is equivalent to

\[de - k \cdot \varphi(n) = 1\]

where \(k \in \ZZ\). From steps 1 and 2, we already know the numeric
values of \(e\) and \(\varphi(n)\). The extended Euclidean algorithm
allows us to compute \(d\) and \(-k\). In Sage, this can be accomplished
via the command xgcd. Given two integers \(x\) and \(y\),
xgcd(x,y) returns a 3-tuple (g,s,t) that satisfies
the Bézout identity \(g = \gcd(x,y) = sx + ty\). Having computed a
value for d, we then use the command
mod(d*e,phi) to check that d*e is indeed congruent
to 1 modulo phi.

Concatenating all the integers in the last table, our message can be
represented by the integer

\[m = 72697676798779827668\]

There are other more cryptographically secure means for representing
our message as an integer. The above process is used for
demonstration purposes only and we strongly discourage its use in
practice. In Sage, we can obtain an integer representation of our
message as follows:

To encrypt our message, we raise \(m\) to the power of \(e\) and reduce
the result modulo \(n\). The command mod(a^b,n) first computes
a^b and then reduces the result modulo n. If the exponent
b is a “large” integer, say with more than 20 digits, then
performing modular exponentiation in this naive manner takes quite
some time. Brute force (or naive) modular exponentiation is
inefficient and, when performed using a computer, can quickly
consume a huge quantity of the computer’s memory or result in overflow
messages. For instance, if we perform naive modular exponentiation
using the command mod(m^e,n), where m, n and e are as
given above, we would get an error message similar to the following:

Thus \(c = 630913632577520058415521090\) is the ciphertext. To recover
our plaintext, we raise c to the power of d and reduce the
result modulo n. Again, we use modular exponentiation via
repeated squaring in the decryption process:

2009-07-25: Ron Evans (Department of Mathematics, UCSD) reported
a typo in the definition of greatest common divisors. The revised
definition incorporates his suggestions.

2008-11-04: Martin Albrecht (Information Security Group, Royal
Holloway, University of London), John Cremona (Mathematics
Institute, University of Warwick) and William Stein (Department of
Mathematics, University of Washington) reviewed this tutorial. Many
of their invaluable suggestions have been incorporated into this
document.