MPI Library Example Code
MPI Library Example Code
This page gives a few examples of how to use the code in the MPI library. There are more interesting
and complete examples in the "utils" subdirectory of the MPI library source distribution, however,
these might help you get started, if you're new to this sort of thing.
#include "mpi.h"
#include "mpprime.h"
In addition, you will need to tell your compiler to link against the compiled MPI object code. How you
do this depends on your C development environment. The simplest thing to do is to build the library,
using:
% make lib
This creates a library file called libmpi.a. Once you have this, you can link it into your program
using your compiler's command line. For instance, using GCC, you might write:
Alternatively, you can build everything on the command line, all at once, e.g.:
However, since mpi.c is a pretty big source file, this could make your compilations take longer than
you might otherwise like. If you don't want to build the library file, I recommend you compile to object
files, and link against those, e.g.:
% make mpi.o
% gcc -ansi -pedantic -Wall -o myprogram mpi.o myprog.c
The advantage of building the library (or using object files) is that, once you have built the library, you
don't need to wait for mpi.c to recompile each time you make a change in your program. Note that
libmpi.a includes both mpi.o and mpprime.o, so if you are trying to get minimal code size, you
may want to build the object files independently, instead of linking against the library file..
If you are building under an environment that doesn't have Unix build tools, such as Metrowerks
CodeWarrior on the Macintosh, you should make sure that the file mpi.h is in the same folder as your
project file, and actually add mpi.c to the project as one of your source files. The CodeWarrior project
manager will take care of recompiling it for you when necessary. (Strictly speaking, as long as they are
in the search path for the project, you can put your header files anywhere you want ... but the project's
folder is included in the search path by default, so that's usually a good place to start).
Reading in Values
Internally, the MPI library represents integer values in signed magnitude format, with a sign flag
indicating whether the number is positive or negative, and an array to hold the digits of the value. But
how do the values get there in the first place?
The mp_read_radix() function provides a general interface for converting numbers which are
stored as text in various bases, into the internal representation of an mp_int. The function is declared
as follows:
The mp parameter should point to the mp_int structure that is to receive the value. This structure
should already be initialized (using the mp_init() function, for example). The str parameter
points to a C style string (nul-terminated) holding the number to be read in. The radix parameter
tells it what base the string is encoded in. This is an integer between 2 and 64 inclusive.
Here is a code snippet which reads in a base 10 number from the command line, and stores it into an
mp_int:
#include "mpi.h"
mp_init(&value);
mp_clear(&value);
return 0;
}
If a leading minus sign (-) appears in the string, the value will be made negative.
mp_read_radix() will stop reading as soon as it encounters a character which is not a valid digit
for the input radix it has been given (so, for example, binary numbers permit 0 and 1, decimal
numbers 0 through 9, etc.).
For bases greater than 10, the letters of the English alphabet stand in for higher-order digits. So, for
example, in hexadecimal (base 16), the letters A through F stand for digit values 10 through 15
respectively. Up to base 36, it does not care whether they are upper- or lower-case, so "C" and "c" are
both considered the same value in bases 11 through 36.
From base 37 to 62, however, the case of the letters matters: The upper case letters represent the
values 10 through 35, and the lower case letters represent the values 36 through 61. For bases 63 and
64, the characters '+' (plus) and '/' (forward solidus or "slash") are used to represent values 62 and 63,
respectively (this is borrowed from the MIME Base64 encoding scheme, although the order of the
characters is slightly different so that the ASCII digits can have their face value).
Another possibility is to read in a value stored as an array of bytes. This is a special case, which the
library calls "raw" format, and it has its own set of routines. To read a "raw" value, use the function:
mp_err mp_read_raw(mp_int *mp, char *str, int len);
As before, mp points to the (initialized) mp_int to receive the value. This time, however, str points
to an array of bytes, and len gives the exact length of that array. The first byte of str is considered
the sign ... if it is zero, the number will be positive (or zero), if it is nonzero, the number will be
negative. The remaining bytes are taken to be base 256 digits, in big-endian (most-significant first)
ordering. This makes the assumption that bytes are 8 bits, but that's not usually a problem.
The mp parameter is the integer value to be converted. The str parameter should point to a buffer
"big enough" to hold this string in the desired base, and radix is an integer from 2 to 64 inclusive,
specifying the base to convert to.
As you can see, you, the caller, are responsible for allocating the memory for the output buffer. How do
you know what constitutes a "big enough" buffer to hold the output of this function? That is the task of
the mp_radix_size() function, which looks like this:
This function returns a size, in bytes, which is sufficient to hold the results of converting mp into the
specified base. The value returned is guaranteed to be big enough, although it is really only an
estimate, so it might be a little bigger than necessary. Still, it will not be more than a few bytes larger
than it should be, so you won't be wasting much memory this way.
Here is a simple code snippet that converts an mp_int into various bases, and prints them out:
#include <stdio.h>
#include <stdlib.h>
#include "mpi.h"
free(obuf);
}
We can get away with only allocating a buffer big enough for the base 10 representation, because we
know that higher bases take the same or fewer digits to represent a given number (for example, 252 in
base ten turns into FC in base 16).
Note:
The mp_toradix() does not modify the mp_int parameter passed to it, so you don't need to make
a copy or anything first.
Also, the string output by mp_toradix() is automatically zero terminated so that it can be
manpulated by the standard C string routines such as strlen(), strcpy(), etc.
These are analogous in function to mp_toradix() and mp_radix_size(), except they convert
the mp_int into an array of raw bytes, rather than a string of printable characters. The first character
is set to zero for a positive (or zero) value, the remaining characters are set to the base 256 digits of the
integer value, in big-endian (most-significant digit first) ordering.
Unlike mp_radix_size(), the buffer size returned by the mp_raw_size() function is exactly
correct. Thus, you can store an mp_int to a binary file using code like this:
#include <stdio.h>
#include "mpi.h"
mp_toraw(mp, buf);
free(buf);
It's up to you, of course, to keep track of this size, so that you can read the value back using
mp_read_raw().
Handling Errors
Most of the example code here does not bother checking for errors. But in a real program, you need to
do that. The MPI library is very consistent about returning error codes from all its important
functions, to indicate success or mode of failure. The mp_err type is used to denote the result code
from the library's public functions.
When an operation succeeds, the library returns MP_OKAY to indicate that the operation completed
successfully. Otherwise, it returns an error code indicating what went wrong. Currently, the following
errors are understood:
MP_OKAY
No error, function completed successfully
MP_YES
Okay, the answer is yes (from a predicate)
MP_NO
Okay, but the answer is no (from a predicate)
MP_MEM
Ran out of memory
MP_RANGE
Argument out of range (e.g. division by zero)
MP_BADARG
An invalid argument was given (e.g. NULL pointer)
MP_UNDEF
The result is undefined
You can either interpret these values yourself, or you can get the library to give you a human-readable
string describing the error in question, using the mp_strerror() function. This is analogous in
function to the strerror() function defined in the standard C library <string.h>, except it only
works with mp_err result codes.
The pointer returned by mp_strerror() points to some static storage in the library, so you don't
need to free it when you're done (and in fact, you shouldn't). Here is a reasonable paradigm for
handling errors from the library:
#include <stdio.h>
#include "mpi.h"
int main(void)
{
mp_err res;
mp_int a;
mp_clear(&a);
return 0;
}
Note that the MPI library doesn't keep around an mp_errno like the C library does with errno, so
even though mp_strerror() is analogous to strerror(), you have to keep track of error codes
yourself, you can't get them back out of a global variable.
Primality Testing
The files mpprime.h and mpprime.c define some extensions to the basic MPI library for use in
primality testing. A prime number is defined as an integer p greater than 1 whose only positive divisors
are 1 and p. Any integer which is not prime is said to be composite. Prime numbers are of great interest
in cryptography, among other things.
When working with very large numbers, it is difficult to determine efficiently whether a number is
actually prime or not. However, there are various probabilistic tests, which let you establish that a
number is prime with some very high degree of probability. That is the kind of primality testing
supported by mpprime.c.
One quick way to tell if a big number is composite is to try dividing it by several small prime numbers.
Since most composite numbers tend to have small prime factors, this lets you quickly rule out "easy"
cases, before bringing in the bigger guns described below. You can do this using the
mpp_divis_primes() function declared in mpprime.h.
The a parameter points to the integer to be tested for divisibility. The np parameter is used both for
input and for output. On input, it should contain the number of primes to try dividing a by. If a turns
out to be divisible by any of them, its value is stored int np when the function returns.
mpp_divis_primes() returns MP_YES if a is divisible by some prime, or MP_NO if it is not.
The primes used are a simple table hard-wired into the library (see the file primes.c). The number
available in the table can be read by consulting the global variable prime_tab_size. So, one
common way to use this function is as follows:
#include <stdio.h>
#include "mpi.h"
#include "mpprime.h"
mp_init(&a);
np = prime_tab_size;
mp_clear(&a);
return 0;
}
Once you have determined that your candidate is not divisible by any small primes, it's time to try
some of the more powerful probabilistic tests. The functions you want are:
The mpp_fermat() function identifies prime numbers using Fermat's "little" theorem, which states
that if p is a prime number, and w is some value which is not a multiple of p, then wp = w (mod p). In
other words, if a doesn't share any common divisors with w, and wa (mod a) is not equal to w, then a is
definitely not prime. If wa (mod a) does equal w, then it is highly probable that a is prime. Generally, a
good value to use for a witness w is some small prime integer which has been verified not to be a
divisor of a. This test is somewhat expensive to compute, so you probably only want to try a couple of
small values, say 2 and 3, for example.
The mpp_pprime() function computes the Rabin-Miller probabilistic primality test for nt
iterations. Without going into details about how it works, if a passes nt iterations of this test, then
there is no more than a 1 in 4nt chance that a is actually composite (and, in fact, the probability is
usually much smaller than that!)
Each of these functions returns MP_YES if the candidate a passes the tests, and MP_NO if it fails. Any
answer of MP_NO (from either function) means the number is definitely composite. You can use the
functions as desired to obtain as high a probability as you wish that a number which appears prime
actually is.
Here is some simple code that generates a probable prime value, given a starting point, using the above
functions:
#include "mpi.h"
#include "mpprime.h"
if(mp_iseven(p)) {
mp_add_d(p, 1, p);
}
do {
mp_digit which = prime_tab_size;
CLEANUP:
return res;
} /* end make_prime() */
To encrypt:
mp_err res;
mp_int e, modulus;
char *msg, *out;
int mlen, olen;
/* ... code to read in e and modulus ... */
Notice the last parameter, myrand. This must be a pointer to a function which can generate
cryptographically secure random non-zero byte strings. The prototype for myrand should be:
When called, myrand() should place len nonzero bytes, chosen at random, into the buffer pointed to
by buf. This is how the library deals with random padding. How you acquire the random values is up to
you (you could use something like Yarrow or a hardware random number generator, if you like).
For a given modulus, there is a maximum allowable size for a single message block, using the RSA
algorithm. Further restrictions are imposed by the PKCS #1 padding requirements. To find out how
many bytes your message may be in length, call:
int maxlen;
maxlen = mp_pkcs1v15_maxlen(&modulus);
To decrypt a ciphertext buffer ctx of length clen, given decryption exponent d, the process is similar:
mp_int d, modulus;
char *msg, *ctx;
int clen, mlen;
mp_err res;