Вы находитесь на странице: 1из 8

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.

• Accessing the Library


• Reading in values
• Writing out values
• Handling errors
• Primality testing

Accessing the Library


To access the functions in the MPI library, you need to include the library's main header file, mpi.h.
If you want to use the primality testing functions, you should also include mpprime.h. If you would
like to use the PKCS#1 routines for RSA encryption and decryption, you should also include
mprsa.h.

#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:

% gcc -ansi -pedantic -Wall -L. -o myprogram myprog.c -lmpi

Alternatively, you can build everything on the command line, all at once, e.g.:

% gcc -ansi -pedantic -Wall -o myprogram mpi.c mpprime.c myprog.c

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:

mp_err mp_read_radix(mp_int *mp, char *str, int radix);

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"

int main(int argc, char *argv[])


{
mp_int value;

mp_init(&value);

mp_read_radix(&value, argv[1], 10);

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.

Writing out Values


The MPI library provides the mp_toradix() function as a general mechanism for converting
mp_int values into printable strings of ASCII digits. The prototype for this function is:

mp_err mp_toradix(mp_int *mp, char *str, int radix);

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:

int mp_radix_size(mp_int *mp, int radix);

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"

void print_int(mp_int *mp)


{
char *obuf;
int size, ix;

size = mp_radix_size(mp, 10);


obuf = malloc(size);

for(ix = 10; ix <= 16; ix++) {


mp_toradix(mp, obuf, ix);
printf("Base %d: %s\n", ix, obuf);
}

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.

To complement the mp_read_raw() function, the library provides:

mp_err mp_toraw(mp_int *mp, char *str);


int mp_raw_size(mp_int mp);

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"

void write_mp_int(mp_int *mp, FILE *ofp)


{
int size = mp_raw_size(mp);
char *buf = malloc(size);

mp_toraw(mp, buf);

fwrite(buf, sizeof(char), size, ofp);

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.

char *mp_strerror(mp_err ec);

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;

if((res = mp_init(&a)) != MP_OKAY) {


fprintf(stderr, "Error: %s\n", mp_strerror(res));
return 1;
}

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.

mp_err mpp_divis_primes(mp_int *a, mp_digit *np);

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"

int main(int argc, char *argv[])


{
mp_int a;
mp_digit np;

mp_init(&a);

np = prime_tab_size;

if(mpp_divis_primes(&a, &np) == MP_YES)


printf("Divisible by small prime %u\n", np);
else
printf("Not divisible by any of the primes tested\n");

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:

mp_err mpp_fermat(mp_int *a, mp_digit w);


mp_err mpp_pprime(mp_int *a, int nt);

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"

mp_err make_prime(mp_int *p, int nr)


{
mp_err res;

if(mp_iseven(p)) {
mp_add_d(p, 1, p);
}

do {
mp_digit which = prime_tab_size;

/* First test for divisibility by a few small primes */


if((res = mpp_divis_primes(p, &which)) == MP_YES)
continue;
else if(res != MP_NO)
goto CLEANUP;

/* If that passes, try one iteration of Fermat's test */


if((res = mpp_fermat(p, 2)) == MP_NO)
continue;
else if(res != MP_YES)
goto CLEANUP;

/* If that passes, run Rabin-Miller as often as requested */


if((res = mpp_pprime(p, nr)) == MP_YES)
break;
else if(res != MP_NO)
goto CLEANUP;

} while((res = mp_add_d(p, 2, p)) == MP_OKAY);

CLEANUP:
return res;

} /* end make_prime() */

RSA Encryption and Decryption


The files mprsa.h and mprsa.c provide simple support for the PKCS #1 standard for encoding and
encryption of messages using the RSA algorithm. Suppose you have a message in the buffer msg whose
length is mlen and which you wish to encrypt. Assume that the public key is stored in two variables of
type mp_int called e and modulus respectively.

To encrypt:

mp_err res;
mp_int e, modulus;
char *msg, *out;
int mlen, olen;
/* ... code to read in e and modulus ... */

res = mp_pkcs1v15_encrypt(msg, mlen, &e, &modulus, &out, &olen, myrand);

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:

void myrand(char *buf, int len);

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;

/* ... read in ciphertext and keys ... */

res = mp_pkcs1v15_decrypt(ctx, clen, &d, &modulus, &msg, &mlen);

Вам также может понравиться