/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2001 Dima Dorfman. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * This is the traditional Berkeley MP library implemented in terms of * the OpenSSL BIGNUM library. It was written to replace libgmp, and * is meant to be as compatible with the latter as feasible. * * There seems to be a lack of documentation for the Berkeley MP * interface. All I could find was libgmp documentation (which didn't * talk about the semantics of the functions) and an old SunOS 4.1 * manual page from 1989. The latter wasn't very detailed, either, * but at least described what the function's arguments were. In * general the interface seems to be archaic, somewhat poorly * designed, and poorly, if at all, documented. It is considered * harmful. * * Miscellaneous notes on this implementation: * * - The SunOS manual page mentioned above indicates that if an error * occurs, the library should "produce messages and core images." * Given that most of the functions don't have return values (and * thus no sane way of alerting the caller to an error), this seems * reasonable. The MPERR and MPERRX macros call warn and warnx, * respectively, then abort(). * * - All the functions which take an argument to be "filled in" * assume that the argument has been initialized by one of the *tom() * routines before being passed to it. I never saw this documented * anywhere, but this seems to be consistent with the way this * library is used. * * - msqrt() is the only routine which had to be implemented which * doesn't have a close counterpart in the OpenSSL BIGNUM library. * It was implemented by hand using Newton's recursive formula. * Doing it this way, although more error-prone, has the positive * sideaffect of testing a lot of other functions; if msqrt() * produces the correct results, most of the other routines will as * well. * * - Internal-use-only routines (i.e., those defined here statically * and not in mp.h) have an underscore prepended to their name (this * is more for aesthetical reasons than technical). All such * routines take an extra argument, 'msg', that denotes what they * should call themselves in an error message. This is so a user * doesn't get an error message from a function they didn't call. */ #include <sys/cdefs.h> #include <ctype.h> #include <err.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <openssl/crypto.h> #include <openssl/err.h> #include "mp.h" #define MPERR(s) do { warn s; abort(); } while (0) #define MPERRX(s) do { warnx s; abort(); } while (0) #define BN_ERRCHECK(msg, expr) do { \ if (!(expr)) _bnerr(msg); \ } while (0) static void _bnerr(const char *); static MINT *_dtom(const char *, const char *); static MINT *_itom(const char *, short); static void _madd(const char *, const MINT *, const MINT *, MINT *); static int _mcmpa(const char *, const MINT *, const MINT *); static void _mdiv(const char *, const MINT *, const MINT *, MINT *, MINT *, BN_CTX *); static void _mfree(const char *, MINT *); static void _moveb(const char *, const BIGNUM *, MINT *); static void _movem(const char *, const MINT *, MINT *); static void _msub(const char *, const MINT *, const MINT *, MINT *); static char *_mtod(const char *, const MINT *); static char *_mtox(const char *, const MINT *); static void _mult(const char *, const MINT *, const MINT *, MINT *, BN_CTX *); static void _sdiv(const char *, const MINT *, short, MINT *, short *, BN_CTX *); static MINT *_xtom(const char *, const char *); /* * Report an error from one of the BN_* functions using MPERRX. */ static void _bnerr(const char *msg) { ERR_load_crypto_strings(); MPERRX(("%s: %s", msg, ERR_reason_error_string(ERR_get_error()))); } /* * Convert a decimal string to an MINT. */ static MINT * _dtom(const char *msg, const char *s) { MINT *mp; mp = malloc(sizeof(*mp)); if (mp == NULL) MPERR(("%s", msg)); mp->bn = BN_new(); if (mp->bn == NULL) _bnerr(msg); BN_ERRCHECK(msg, BN_dec2bn(&mp->bn, s)); return (mp); } /* * Compute the greatest common divisor of mp1 and mp2; result goes in rmp. */ void mp_gcd(const MINT *mp1, const MINT *mp2, MINT *rmp) { BIGNUM *b; BN_CTX *c; b = NULL; c = BN_CTX_new(); if (c != NULL) b = BN_new(); if (c == NULL || b == NULL) _bnerr("gcd"); BN_ERRCHECK("gcd", BN_gcd(b, mp1->bn, mp2->bn, c)); _moveb("gcd", b, rmp); BN_free(b); BN_CTX_free(c); } /* * Make an MINT out of a short integer. Return value must be mfree()'d. */ static MINT * _itom(const char *msg, short n) { MINT *mp; char *s; asprintf(&s, "%x", n); if (s == NULL) MPERR(("%s", msg)); mp = _xtom(msg, s); free(s); return (mp); } MINT * mp_itom(short n) { return (_itom("itom", n)); } /* * Compute rmp=mp1+mp2. */ static void _madd(const char *msg, const MINT *mp1, const MINT *mp2, MINT *rmp) { BIGNUM *b; b = BN_new(); if (b == NULL) _bnerr(msg); BN_ERRCHECK(msg, BN_add(b, mp1->bn, mp2->bn)); _moveb(msg, b, rmp); BN_free(b); } void mp_madd(const MINT *mp1, const MINT *mp2, MINT *rmp) { _madd("madd", mp1, mp2, rmp); } /* * Return -1, 0, or 1 if mp1<mp2, mp1==mp2, or mp1>mp2, respectivley. */ int mp_mcmp(const MINT *mp1, const MINT *mp2) { return (BN_cmp(mp1->bn, mp2->bn)); } /* * Same as mcmp but compares absolute values. */ static int _mcmpa(const char *msg __unused, const MINT *mp1, const MINT *mp2) { return (BN_ucmp(mp1->bn, mp2->bn)); } /* * Compute qmp=nmp/dmp and rmp=nmp%dmp. */ static void _mdiv(const char *msg, const MINT *nmp, const MINT *dmp, MINT *qmp, MINT *rmp, BN_CTX *c) { BIGNUM *q, *r; q = NULL; r = BN_new(); if (r != NULL) q = BN_new(); if (r == NULL || q == NULL) _bnerr(msg); BN_ERRCHECK(msg, BN_div(q, r, nmp->bn, dmp->bn, c)); _moveb(msg, q, qmp); _moveb(msg, r, rmp); BN_free(q); BN_free(r); } void mp_mdiv(const MINT *nmp, const MINT *dmp, MINT *qmp, MINT *rmp) { BN_CTX *c; c = BN_CTX_new(); if (c == NULL) _bnerr("mdiv"); _mdiv("mdiv", nmp, dmp, qmp, rmp, c); BN_CTX_free(c); } /* * Free memory associated with an MINT. */ static void _mfree(const char *msg __unused, MINT *mp) { BN_clear(mp->bn); BN_free(mp->bn); free(mp); } void mp_mfree(MINT *mp) { _mfree("mfree", mp); } /* * Read an integer from standard input and stick the result in mp. * The input is treated to be in base 10. This must be the silliest * API in existence; why can't the program read in a string and call * xtom()? (Or if base 10 is desires, perhaps dtom() could be * exported.) */ void mp_min(MINT *mp) { MINT *rmp; char *line, *nline; size_t linelen; line = fgetln(stdin, &linelen); if (line == NULL) MPERR(("min")); nline = malloc(linelen + 1); if (nline == NULL) MPERR(("min")); memcpy(nline, line, linelen); nline[linelen] = '\0'; rmp = _dtom("min", nline); _movem("min", rmp, mp); _mfree("min", rmp); free(nline); } /* * Print the value of mp to standard output in base 10. See blurb * above min() for why this is so useless. */ void mp_mout(const MINT *mp) { char *s; s = _mtod("mout", mp); printf("%s", s); free(s); } /* * Set the value of tmp to the value of smp (i.e., tmp=smp). */ void mp_move(const MINT *smp, MINT *tmp) { _movem("move", smp, tmp); } /* * Internal routine to set the value of tmp to that of sbp. */ static void _moveb(const char *msg, const BIGNUM *sbp, MINT *tmp) { BN_ERRCHECK(msg, BN_copy(tmp->bn, sbp)); } /* * Internal routine to set the value of tmp to that of smp. */ static void _movem(const char *msg, const MINT *smp, MINT *tmp) { BN_ERRCHECK(msg, BN_copy(tmp->bn, smp->bn)); } /* * Compute the square root of nmp and put the result in xmp. The * remainder goes in rmp. Should satisfy: rmp=nmp-(xmp*xmp). * * Note that the OpenSSL BIGNUM library does not have a square root * function, so this had to be implemented by hand using Newton's * recursive formula: * * x = (x + (n / x)) / 2 * * where x is the square root of the positive number n. In the * beginning, x should be a reasonable guess, but the value 1, * although suboptimal, works, too; this is that is used below. */ void mp_msqrt(const MINT *nmp, MINT *xmp, MINT *rmp) { BN_CTX *c; MINT *tolerance; MINT *ox, *x; MINT *z1, *z2, *z3; short i; c = BN_CTX_new(); if (c == NULL) _bnerr("msqrt"); tolerance = _itom("msqrt", 1); x = _itom("msqrt", 1); ox = _itom("msqrt", 0); z1 = _itom("msqrt", 0); z2 = _itom("msqrt", 0); z3 = _itom("msqrt", 0); do { _movem("msqrt", x, ox); _mdiv("msqrt", nmp, x, z1, z2, c); _madd("msqrt", x, z1, z2); _sdiv("msqrt", z2, 2, x, &i, c); _msub("msqrt", ox, x, z3); } while (_mcmpa("msqrt", z3, tolerance) == 1); _movem("msqrt", x, xmp); _mult("msqrt", x, x, z1, c); _msub("msqrt", nmp, z1, z2); _movem("msqrt", z2, rmp); _mfree("msqrt", tolerance); _mfree("msqrt", ox); _mfree("msqrt", x); _mfree("msqrt", z1); _mfree("msqrt", z2); _mfree("msqrt", z3); BN_CTX_free(c); } /* * Compute rmp=mp1-mp2. */ static void _msub(const char *msg, const MINT *mp1, const MINT *mp2, MINT *rmp) { BIGNUM *b; b = BN_new(); if (b == NULL) _bnerr(msg); BN_ERRCHECK(msg, BN_sub(b, mp1->bn, mp2->bn)); _moveb(msg, b, rmp); BN_free(b); } void mp_msub(const MINT *mp1, const MINT *mp2, MINT *rmp) { _msub("msub", mp1, mp2, rmp); } /* * Return a decimal representation of mp. Return value must be * free()'d. */ static char * _mtod(const char *msg, const MINT *mp) { char *s, *s2; s = BN_bn2dec(mp->bn); if (s == NULL) _bnerr(msg); asprintf(&s2, "%s", s); if (s2 == NULL) MPERR(("%s", msg)); OPENSSL_free(s); return (s2); } /* * Return a hexadecimal representation of mp. Return value must be * free()'d. */ static char * _mtox(const char *msg, const MINT *mp) { char *p, *s, *s2; int len; s = BN_bn2hex(mp->bn); if (s == NULL) _bnerr(msg); asprintf(&s2, "%s", s); if (s2 == NULL) MPERR(("%s", msg)); OPENSSL_free(s); /* * This is a kludge for libgmp compatibility. The latter's * implementation of this function returns lower-case letters, * but BN_bn2hex returns upper-case. Some programs (e.g., * newkey(1)) are sensitive to this. Although it's probably * their fault, it's nice to be compatible. */ len = strlen(s2); for (p = s2; p < s2 + len; p++) *p = tolower(*p); return (s2); } char * mp_mtox(const MINT *mp) { return (_mtox("mtox", mp)); } /* * Compute rmp=mp1*mp2. */ static void _mult(const char *msg, const MINT *mp1, const MINT *mp2, MINT *rmp, BN_CTX *c) { BIGNUM *b; b = BN_new(); if (b == NULL) _bnerr(msg); BN_ERRCHECK(msg, BN_mul(b, mp1->bn, mp2->bn, c)); _moveb(msg, b, rmp); BN_free(b); } void mp_mult(const MINT *mp1, const MINT *mp2, MINT *rmp) { BN_CTX *c; c = BN_CTX_new(); if (c == NULL) _bnerr("mult"); _mult("mult", mp1, mp2, rmp, c); BN_CTX_free(c); } /* * Compute rmp=(bmp^emp)mod mmp. (Note that here and above rpow() '^' * means 'raise to power', not 'bitwise XOR'.) */ void mp_pow(const MINT *bmp, const MINT *emp, const MINT *mmp, MINT *rmp) { BIGNUM *b; BN_CTX *c; b = NULL; c = BN_CTX_new(); if (c != NULL) b = BN_new(); if (c == NULL || b == NULL) _bnerr("pow"); BN_ERRCHECK("pow", BN_mod_exp(b, bmp->bn, emp->bn, mmp->bn, c)); _moveb("pow", b, rmp); BN_free(b); BN_CTX_free(c); } /* * Compute rmp=bmp^e. (See note above pow().) */ void mp_rpow(const MINT *bmp, short e, MINT *rmp) { MINT *emp; BIGNUM *b; BN_CTX *c; b = NULL; c = BN_CTX_new(); if (c != NULL) b = BN_new(); if (c == NULL || b == NULL) _bnerr("rpow"); emp = _itom("rpow", e); BN_ERRCHECK("rpow", BN_exp(b, bmp->bn, emp->bn, c)); _moveb("rpow", b, rmp); _mfree("rpow", emp); BN_free(b); BN_CTX_free(c); } /* * Compute qmp=nmp/d and ro=nmp%d. */ static void _sdiv(const char *msg, const MINT *nmp, short d, MINT *qmp, short *ro, BN_CTX *c) { MINT *dmp, *rmp; BIGNUM *q, *r; char *s; r = NULL; q = BN_new(); if (q != NULL) r = BN_new(); if (q == NULL || r == NULL) _bnerr(msg); dmp = _itom(msg, d); rmp = _itom(msg, 0); BN_ERRCHECK(msg, BN_div(q, r, nmp->bn, dmp->bn, c)); _moveb(msg, q, qmp); _moveb(msg, r, rmp); s = _mtox(msg, rmp); errno = 0; *ro = strtol(s, NULL, 16); if (errno != 0) MPERR(("%s underflow or overflow", msg)); free(s); _mfree(msg, dmp); _mfree(msg, rmp); BN_free(r); BN_free(q); } void mp_sdiv(const MINT *nmp, short d, MINT *qmp, short *ro) { BN_CTX *c; c = BN_CTX_new(); if (c == NULL) _bnerr("sdiv"); _sdiv("sdiv", nmp, d, qmp, ro, c); BN_CTX_free(c); } /* * Convert a hexadecimal string to an MINT. */ static MINT * _xtom(const char *msg, const char *s) { MINT *mp; mp = malloc(sizeof(*mp)); if (mp == NULL) MPERR(("%s", msg)); mp->bn = BN_new(); if (mp->bn == NULL) _bnerr(msg); BN_ERRCHECK(msg, BN_hex2bn(&mp->bn, s)); return (mp); } MINT * mp_xtom(const char *s) { return (_xtom("xtom", s)); }