From be31515f5e9aa629a42e22a1055b5ac3fd814ed1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dag-Erling=20Sm=C3=B8rgrav?= Date: Tue, 12 Aug 2014 07:30:31 +0000 Subject: [PATCH] Start of a multiple-precision integer arithmetic library. --- configure.ac | 1 + include/cryb/Makefile.am | 1 + include/cryb/mpi.h | 57 +++ lib/Makefile.am | 1 + lib/mpi/Makefile.am | 10 + lib/mpi/README | 16 + lib/mpi/mpi.c | 234 ++++++++++ lib/mpi/mpi_add.c | 83 ++++ lib/mpi/mpi_cmp.c | 82 ++++ lib/mpi/mpi_shift.c | 111 +++++ lib/mpi/mpi_sub.c | 83 ++++ t/Makefile.am | 4 + t/t_mpi.c | 904 +++++++++++++++++++++++++++++++++++++++ 13 files changed, 1587 insertions(+) create mode 100644 include/cryb/mpi.h create mode 100644 lib/mpi/Makefile.am create mode 100644 lib/mpi/README create mode 100644 lib/mpi/mpi.c create mode 100644 lib/mpi/mpi_add.c create mode 100644 lib/mpi/mpi_cmp.c create mode 100644 lib/mpi/mpi_shift.c create mode 100644 lib/mpi/mpi_sub.c create mode 100644 t/t_mpi.c diff --git a/configure.ac b/configure.ac index 0c2cf59..c688a6e 100644 --- a/configure.ac +++ b/configure.ac @@ -123,6 +123,7 @@ AC_CONFIG_FILES([ lib/enc/Makefile lib/hash/Makefile lib/mac/Makefile + lib/mpi/Makefile lib/oath/Makefile lib/rand/Makefile lib/rsaref/Makefile diff --git a/include/cryb/Makefile.am b/include/cryb/Makefile.am index 15bb20a..48f39a7 100644 --- a/include/cryb/Makefile.am +++ b/include/cryb/Makefile.am @@ -18,6 +18,7 @@ cryb_HEADERS = \ md2.h \ md4.h \ md5.h \ + mpi.h \ oath.h \ oath_constants.h \ oath_types.h \ diff --git a/include/cryb/mpi.h b/include/cryb/mpi.h new file mode 100644 index 0000000..8020a09 --- /dev/null +++ b/include/cryb/mpi.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2014 Dag-Erling Smørgrav + * 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. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * 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. + */ + +#ifndef CRYB_MPI_H_INCLUDED +#define CRYB_MPI_H_INCLUDED + +#define CRYB_MPI_SWORDS 16 + +typedef struct cryb_mpi { + uint32_t *words; /* pointer to words */ + size_t size; /* number of words */ + unsigned int msb; /* most significant bit */ + unsigned int neg:1; /* negative */ + uint32_t swords[CRYB_MPI_SWORDS]; +} cryb_mpi; + +void mpi_init(cryb_mpi *); +void mpi_destroy(cryb_mpi *); +int mpi_grow(cryb_mpi *, unsigned int); +void mpi_zero(cryb_mpi *); +void mpi_negate(cryb_mpi *); +int mpi_copy(cryb_mpi *, const cryb_mpi *); +void mpi_swap(cryb_mpi *, cryb_mpi *); +int mpi_load(cryb_mpi *, const uint8_t *, size_t); +int mpi_set(cryb_mpi *, int32_t); +int mpi_lshift(cryb_mpi *, unsigned int); +int mpi_rshift(cryb_mpi *, unsigned int); +int mpi_add_abs(cryb_mpi *, cryb_mpi *, cryb_mpi *); +int mpi_sub_abs(cryb_mpi *, cryb_mpi *, cryb_mpi *); + +#endif diff --git a/lib/Makefile.am b/lib/Makefile.am index dfd7a91..294fcbb 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -5,6 +5,7 @@ SUBDIRS = \ enc \ hash \ mac \ + mpi \ rand \ oath diff --git a/lib/mpi/Makefile.am b/lib/mpi/Makefile.am new file mode 100644 index 0000000..cf732bb --- /dev/null +++ b/lib/mpi/Makefile.am @@ -0,0 +1,10 @@ +AM_CPPFLAGS = -I$(top_srcdir)/include + +lib_LTLIBRARIES = libcryb-mpi.la + +libcryb_mpi_la_SOURCES = \ + mpi.c \ + mpi_add.c \ + mpi_sub.c \ + mpi_shift.c \ + mpi_cmp.c diff --git a/lib/mpi/README b/lib/mpi/README new file mode 100644 index 0000000..17efb27 --- /dev/null +++ b/lib/mpi/README @@ -0,0 +1,16 @@ +Portable multiple-precision integer library. + +The interface is loosely based on XySSL's MPI library. The +implementation was written from scratch based on the algorithms +described in Menezes, van Oorschot and Vanstone: _Handbook of Applied +Cryptography_, CRC Press, 1996. The full text of the HAC is available +for personal use at http://www.cacr.math.uwaterloo.ca/hac/. + +This implementation sacrifices performance for portability and +readability, but attempts have been made to structure it in such a way +that critical portions can easily be replaced with machine-dependent +optimized versions. + +The code assumes a 64-bit machine or a 32-bit machine where 64-bit +integer arithmetic is supported but not necessarily efficient; 64-bit +operations are used only to simplify overflow handling. diff --git a/lib/mpi/mpi.c b/lib/mpi/mpi.c new file mode 100644 index 0000000..58976b8 --- /dev/null +++ b/lib/mpi/mpi.c @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2014 Dag-Erling Smørgrav + * 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. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * 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. + */ + +#include "cryb/impl.h" + +#include +#include +#include +#include + +#include + +/* n rounded up to nearest multiple of p */ +#define RUP(n, p) ((((n) + (p) - 1) / (p)) * (p)) + +/* + * Initialize an all-zeroes mpi. + */ +#define MPI_FAST_INIT(X) \ + do { \ + (X)->words = (X)->swords; \ + (X)->size = CRYB_MPI_SWORDS; \ + } while (0) + +/* + * Initialize an mpi. + */ +void +mpi_init(cryb_mpi *X) +{ + + memset(X, 0, sizeof *X); + MPI_FAST_INIT(X); +} + +/* + * Destroy an mpi. + */ +void +mpi_destroy(cryb_mpi *X) +{ + + if (X->words != NULL && X->words != X->swords) { + memset(X->words, 0, X->size); + free(X->words); + } + mpi_init(X); +} + +/* + * Grow the underlying storage to fit at least n bits + */ +int +mpi_grow(cryb_mpi *X, unsigned int n) +{ + uint32_t *words; + size_t size; + + if (X->words == NULL) + MPI_FAST_INIT(X); + size = (n + 31) / 32; + if (X->size >= size) + return (0); + size = RUP(size, CRYB_MPI_SWORDS); + if ((words = calloc(1, size * sizeof *words)) == NULL) + return (-1); + memcpy(words, X->words, X->size * sizeof *words); + memset(X->words, 0, X->size * sizeof *words); + if (X->words != X->swords) + free(X->words); + X->words = words; + X->size = size; + return (0); +} + +/* + * Set X to zero + */ +void +mpi_zero(cryb_mpi *X) +{ + + if (X->words == NULL) { + MPI_FAST_INIT(X); + } else { + memset(X->words, 0, X->size); + X->neg = 0; + X->msb = 0; + } +} + +/* + * Flip sign + */ +void +mpi_negate(cryb_mpi *X) +{ + + if (X->msb > 0) + X->neg = !X->neg; +} + +/* + * Copy the contents of Y into X + */ +int +mpi_copy(cryb_mpi *X, const cryb_mpi *Y) +{ + + if (X == Y) + return (0); + mpi_zero(X); + if (mpi_grow(X, Y->msb) != 0) + return (-1); + X->msb = Y->msb; + X->neg = Y->neg; + memcpy(X->words, Y->words, X->size * sizeof *X->words); + return (0); +} + +/* + * Swap the contents of X and Y + */ +void +mpi_swap(cryb_mpi *X, cryb_mpi *Y) +{ + cryb_mpi T; + + memcpy(&T, X, sizeof T); + memcpy(X, Y, sizeof *X); + memcpy(Y, &T, sizeof *Y); + if (X->words == Y->swords) + X->words = X->swords; + if (Y->words == X->swords) + Y->words = Y->swords; +} + +/* + * Load value from an array of bytes + */ +int +mpi_load(cryb_mpi *X, const uint8_t *a, size_t len) +{ + int i; + + mpi_zero(X); + /* skip zeroes */ + while (len > 0 && *a == 0) + --len, ++a; + if (len == 0) + return (0); + /* make room */ + if (mpi_grow(X, len * 8) != 0) + return (-1); + /* load whole words */ + for (i = 0; len >= 4; ++i, len -= 4) + X->words[i] = + a[len - 4] << 24 | + a[len - 3] << 16 | + a[len - 2] << 8 | + a[len - 1]; + /* load remaining bytes */ + switch (len) { + case 3: + X->words[i] |= a[len - 3] << 16; + case 2: + X->words[i] |= a[len - 2] << 8; + case 1: + X->words[i] |= a[len - 1]; + break; + case 0: + --i; + CRYB_NO_DEFAULT_CASE; + } + /* i now points to the msw */ + /* compute msb of msw */ + for (X->msb = 31; X->msb > 0; --X->msb) + if (X->words[i] & (1 << X->msb)) + break; + /* add msw offset */ + X->msb += i * 32 + 1; + return (0); +} + +/* + * Set value of X from integer z + */ +int +mpi_set(cryb_mpi *X, int32_t z) +{ + uint32_t zabs; + + mpi_zero(X); + if (mpi_grow(X, sizeof z * 8) != 0) + return (-1); + if (z < 0) { + X->neg = 1; + zabs = -z; + } else { + zabs = z; + } + X->words[0] = zabs; + while (zabs > 0) { + X->msb++; + zabs >>= 1; + } + return (0); +} diff --git a/lib/mpi/mpi_add.c b/lib/mpi/mpi_add.c new file mode 100644 index 0000000..06f063f --- /dev/null +++ b/lib/mpi/mpi_add.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2014 Dag-Erling Smørgrav + * 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. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * 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. + */ + +#include "cryb/impl.h" + +#include +#include + +#include + +/* + * Store the sum of the absolutes values of A and B in X. + * XXX safe for either A or B to be the same object as X but not both + */ +int +mpi_add_abs(cryb_mpi *X, cryb_mpi *A, cryb_mpi *B) +{ + unsigned int i; + uint32_t c; + + if (X == B) + B = A, A = X; + if (X != A) + mpi_copy(X, A); + /* 0 + 0 = 0 */ + if (X->msb == 0 && B->msb == 0) + return (0); + /* make room */ + if (mpi_grow(X, X->msb + 1) != 0 || mpi_grow(X, B->msb + 1) != 0) + return (-1); + /* add B into X word by word until we run out of B */ + for (c = i = 0; i < (B->msb + 31) / 32; ++i) { + // fprintf(stderr, "0x%08x + 0x%08x (%u)", X->words[i], B->words[i], c); + X->words[i] += c; + c = (X->words[i] < c); + X->words[i] += B->words[i]; + c += (X->words[i] < B->words[i]); + // fprintf(stderr, " = 0x%08x (%u)\n", X->words[i], c); + } + /* keep propagating carry */ + while (c) { + // fprintf(stderr, "0x%08x + 0x%08x (%u)", X->words[i], 0, c); + X->words[i] += c; + c = (X->words[i] < c); + // fprintf(stderr, " = 0x%08x (%u)\n", X->words[i], c); + ++i; + } + if (X->words[i] == 0) + --i; + /* compute msb of msw */ + for (X->msb = 31; X->msb > 0; --X->msb) + if (X->words[i] & (1 << X->msb)) + break; + /* add msw offset */ + X->msb += i * 32 + 1; + return (0); +} diff --git a/lib/mpi/mpi_cmp.c b/lib/mpi/mpi_cmp.c new file mode 100644 index 0000000..83fa30c --- /dev/null +++ b/lib/mpi/mpi_cmp.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2014 Dag-Erling Smørgrav + * 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. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * 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. + */ + +#include "cryb/impl.h" + +#include +#include + +#include + +/* n rounded up to nearest multiple of p */ +#define RUP(n, p) ((((n) + (p) - 1) / (p)) * (p)) + +/* + * Compare absolute values + */ +int +mpi_cmp_abs(cryb_mpi *X, cryb_mpi *Y) +{ + int i; + + /* check width first */ + if (X->msb > Y->msb) + return (1); + if (X->msb < Y->msb) + return (-1); + /* no luck, compare word by word */ + for (i = X->msb / 32; i >= 0; --i) { + if (X->words[i] > Y->words[i]) + return (1); + if (X->words[i] < Y->words[i]) + return (-1); + } + return (0); +} + +/* + * Compare signed values + */ +int +mpi_cmp(cryb_mpi *X, cryb_mpi *Y) +{ + int i; + + if (X->neg) { + if (Y->neg) + return (-mpi_cmp_abs(X, Y)); + else + return (-1); + } else { + if (Y->neg) + return (1); + else + return (mpi_cmp_abs(X, Y)); + } +} diff --git a/lib/mpi/mpi_shift.c b/lib/mpi/mpi_shift.c new file mode 100644 index 0000000..f058eff --- /dev/null +++ b/lib/mpi/mpi_shift.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2014 Dag-Erling Smørgrav + * 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. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * 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. + */ + +#include "cryb/impl.h" + +#include +#include + +#include + +/* + * Left-shift X by c bytes + */ +int +mpi_lshift(cryb_mpi *X, unsigned int c) +{ + unsigned int cl, i; + + /* zero shift */ + if (c == 0) + return (0); + /* make room */ + if (mpi_grow(X, X->msb + c) != 0) + return (-1); + /* preemptively adjust msb */ + X->msb += c; + /* try to move whole words first */ + if (c >= 32) { + cl = c / 32; + c = c % 32; + memmove(X->words + cl, X->words, + (X->size - cl) * sizeof *X->words); + memset(X->words, 0, cl * sizeof *X->words); + } + /* done? */ + if (c == 0) + return (0); + /* the rest has to be done the hard way */ + for (i = X->msb / 32; i > 0; --i) { + X->words[i] <<= c; + X->words[i] |= X->words[i - 1] >> (32 - c); + } + X->words[0] <<= c; + /* done! */ + return (0); +} + +/* + * Right-shift: X >>= c + */ +int +mpi_rshift(cryb_mpi *X, unsigned int c) +{ + unsigned int cl, i; + + /* zero shift */ + if (c == 0) + return (0); + /* shift wider than number, result is zero */ + if (X->msb <= c) { + mpi_zero(X); + return (0); + } + /* preemptively adjust msb */ + X->msb -= c; + /* try to move whole words first */ + if (c >= 32) { + cl = c / 32; + c = c % 32; + memmove(X->words, X->words + cl, + (X->size - cl) * sizeof *X->words); + memset(X->words + X->size - cl, 0, cl * sizeof *X->words); + } + /* done? */ + if (c == 0) + return (0); + /* the rest has to be done the hard way */ + for (i = 0; i < X->size - 1; ++i) { + X->words[i] >>= c; + X->words[i] |= X->words[i + 1] << (32 - c); + } + X->words[X->size - 1] >>= c; + /* done! */ + return (0); +} diff --git a/lib/mpi/mpi_sub.c b/lib/mpi/mpi_sub.c new file mode 100644 index 0000000..dc3130f --- /dev/null +++ b/lib/mpi/mpi_sub.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2014 Dag-Erling Smørgrav + * 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. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * 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. + */ + +#include "cryb/impl.h" + +#include +#include + +#include + +/* + * Store the difference between the absolute values of A and B in X. + * XXX safe for either A or B to be the same object as X but not both + */ +int +mpi_sub_abs(cryb_mpi *X, cryb_mpi *A, cryb_mpi *B) +{ + unsigned int i; + uint32_t c; + + if (X == B) + B = A, A = X; + if (X != A) + mpi_copy(X, A); + /* 0 - 0 = 0 */ + if (X->msb == 0 && B->msb == 0) + return (0); + /* make room */ + if (mpi_grow(X, X->msb) != 0 || mpi_grow(X, B->msb) != 0) + return (-1); + /* subtract B from X word by word until we run out of B */ + for (c = i = 0; i < (B->msb + 31) / 32; ++i) { + // fprintf(stderr, "0x%08x + 0x%08x (%u)", X->words[i], B->words[i], c); + X->words[i] -= c; + c = (X->words[i] > c); + X->words[i] -= B->words[i]; + c += (X->words[i] > B->words[i]); + // fprintf(stderr, " = 0x%08x (%u)\n", X->words[i], c); + } + /* keep propagating carry */ + while (c) { + // fprintf(stderr, "0x%08x + 0x%08x (%u)", X->words[i], 0, c); + X->words[i] -= c; + c = (X->words[i] > c); + // fprintf(stderr, " = 0x%08x (%u)\n", X->words[i], c); + ++i; + } + if (X->words[i] == 0) + --i; + /* compute msb of msw */ + for (X->msb = 31; X->msb > 0; --X->msb) + if (X->words[i] & (1 << X->msb)) + break; + /* add msw offset */ + X->msb += i * 32 + 1; + return (0); +} diff --git a/t/Makefile.am b/t/Makefile.am index 205ef86..3efeb2d 100644 --- a/t/Makefile.am +++ b/t/Makefile.am @@ -14,6 +14,7 @@ LDADD = $(builddir)/libt.la \ $(top_builddir)/lib/digest/libcryb-digest.la \ $(top_builddir)/lib/hash/libcryb-hash.la \ $(top_builddir)/lib/mac/libcryb-mac.la \ + $(top_builddir)/lib/mpi/libcryb-mpi.la \ $(top_builddir)/lib/rand/libcryb-rand.la \ $(top_builddir)/lib/oath/libcryb-oath.la @@ -107,4 +108,7 @@ t_hmac_sha512_openssl_CFLAGS = $(OPENSSL_INCLUDES) $(OPENSSL_CFLAGS) t_hmac_sha512_openssl_LDADD = $(OPENSSL_LDADD) endif +# libcryb-mpi +TESTS += t_mpi + check_PROGRAMS = $(TESTS) diff --git a/t/t_mpi.c b/t/t_mpi.c new file mode 100644 index 0000000..f3259b0 --- /dev/null +++ b/t/t_mpi.c @@ -0,0 +1,904 @@ +/* + * Copyright (c) 2014 Dag-Erling Smørgrav + * 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. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * 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. + */ + +#include "cryb/impl.h" + +#include +#include +#include + +#include + +#include "t.h" + + +/*************************************************************************** + * Useful constants + */ + +static cryb_mpi z; +#define LARGE_V_SIZE 256 +#define LARGE_E_SIZE 64 +static uint8_t large_v[LARGE_V_SIZE]; +static uint32_t large_e[LARGE_E_SIZE]; + + +/*************************************************************************** + * Commonly used predicates + */ + +/* + * Verify that an MPI has never grown. + */ +static int +t_mpi_not_grown(cryb_mpi *x) +{ + int ret = 1; + + ret &= t_compare_ptr(x->swords, x->words); + ret &= t_compare_sz(CRYB_MPI_SWORDS, x->size); + return (ret); +} + +/* + * Verify that an MPI has grown. + */ +static int +t_mpi_grown(cryb_mpi *x) +{ + int ret = 1; + + /* XXX we need inequality predicates */ + if (x->words == x->swords) { + t_verbose("value was expected to change"); + ret &= 0; + } + if (x->size == CRYB_MPI_SWORDS) { + t_verbose("value was expected to change"); + ret &= 0; + } + return (ret); +} + +/* + * Verify that an MPI is zero. + */ +static int +t_mpi_is_zero(cryb_mpi *x) +{ + int ret = 1; + + ret &= t_mpi_not_grown(x); + ret &= t_compare_mem(t_zero, x->words, CRYB_MPI_SWORDS); + ret &= t_compare_u(0, x->msb); + ret &= t_compare_i(0, x->neg); + return (ret); +} + + +/*************************************************************************** + * Miscellaneous cases + */ + +/* + * Test MPI initialization. + */ +static int +t_mpi_init(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + cryb_mpi x; + + memset(&x, 0xff, sizeof x); + mpi_init(&x); + return (t_mpi_is_zero(&x)); +} + +/* + * Very basic value-setting test, zero. + */ +static int +t_mpi_set_zero(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + cryb_mpi x; + + mpi_init(&x); + mpi_set(&x, 0); + return (t_mpi_is_zero(&x)); +} + +/* + * As above, but use the "fast init" logic whereby an all-zeroes MPI is + * automatically converted to a valid MPI representing the value zero. + */ +static int +t_mpi_fast_init(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + cryb_mpi x = { }; + + mpi_set(&x, 0); + return (t_mpi_is_zero(&x)); +} + +/* + * Test successful MPI growth. + */ +static int +t_mpi_grow_ok(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + cryb_mpi x = { }; + int ret = 1; + + mpi_set(&x, 1); + assert(x.words[0] == 1 && x.msb == 1); + ret &= t_compare_i(0, mpi_grow(&x, CRYB_MPI_SWORDS * 32 + 1)); + assert(x.words[0] == 1 && x.msb == 1); + ret &= t_mpi_grown(&x); + mpi_destroy(&x); + return (ret); +} + +/* + * Test failed MPI growth. + */ +static int +t_mpi_grow_fail(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + cryb_mpi x = { }; + int ret = 1; + + ++t_malloc_fail; + ret &= t_compare_i(-1, mpi_grow(&x, CRYB_MPI_SWORDS * 32 + 1)); + --t_malloc_fail; + ret &= t_mpi_not_grown(&x); + mpi_destroy(&x); + return (ret); +} + +/* + * Double successful MPI growth. + */ +static int +t_mpi_grow_twice(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + cryb_mpi x = { }; + uint32_t *p; + int ret = 1; + + ret &= t_compare_i(0, mpi_grow(&x, CRYB_MPI_SWORDS * 32 + 1)); + ret &= t_mpi_grown(&x); + p = x.words; + ret &= t_compare_i(0, mpi_grow(&x, CRYB_MPI_SWORDS * 32 * 2 + 1)); + /* XXX we need inequality predicates */ + if (x.words == p) { + t_verbose("value was expected to change"); + ret &= 0; + } + ret &= t_mpi_grown(&x); + return (ret); +} + +/* + * Test the destruction logic with an uninitialized MPI + */ +static int +t_mpi_destroy_uninit(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + cryb_mpi x = { }; + + mpi_destroy(&x); + return (t_mpi_is_zero(&x)); +} + +/* + * Test the destruction logic with an MPI that hasn't grown + */ +static int +t_mpi_destroy_static(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + cryb_mpi x = { }; + + assert(sizeof large_v >= sizeof x.swords); + mpi_load(&x, large_v, sizeof x.swords); + assert(x.words == x.swords); + mpi_destroy(&x); + return (t_mpi_is_zero(&x)); +} + +/* + * Test the destruction logic with an MPI that has grown + */ +static int +t_mpi_destroy_grown(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + cryb_mpi x = { }; + + assert(sizeof large_v > sizeof x.swords); + mpi_load(&x, large_v, sizeof large_v); + assert(x.words != x.swords); + mpi_destroy(&x); + return (t_mpi_is_zero(&x)); +} + + +/*************************************************************************** + * Assignment, negation, copying, swapping + */ + +/* + * Assign a positive value. + */ +static int +t_mpi_set_positive(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + cryb_mpi x; + int ret = 1; + + mpi_init(&x); + mpi_set(&x, 0x19700101); + ret &= t_mpi_not_grown(&x); + ret &= t_compare_x32(0x19700101, x.words[0]); + ret &= t_compare_u(29, x.msb); + ret &= t_compare_i(0, x.neg); + return (ret); +} + +/* + * Assign a negative value. + */ +static int +t_mpi_set_negative(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + cryb_mpi x; + int ret = 1; + + mpi_init(&x); + mpi_set(&x, -0x19700101); + ret &= t_mpi_not_grown(&x); + ret &= t_compare_x32(0x19700101, x.words[0]); + ret &= t_compare_u(29, x.msb); + ret &= t_compare_u(1, x.neg); + return (ret); +} + +/* + * Negate zero + */ +static int +t_mpi_negate_zero(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + cryb_mpi x = { }; + + mpi_zero(&x); + assert(x.words[0] == 0 && x.msb == 0 && x.neg == 0); + mpi_negate(&x); + return (t_mpi_is_zero(&x)); +} + +/* + * Negate non-zero, back and forth + */ +static int +t_mpi_negate_nonzero(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + cryb_mpi x = { }; + int ret = 1; + + mpi_set(&x, -0x19700101); + ret &= t_compare_x32(0x19700101, x.words[0]); + assert(x.words[0] == 0x19700101 && x.msb == 29 && x.neg == 1); + mpi_negate(&x); + ret &= t_mpi_not_grown(&x); + ret &= t_compare_x32(0x19700101, x.words[0]); + ret &= t_compare_u(29, x.msb); + ret &= t_compare_u(0, x.neg); + mpi_negate(&x); + ret &= t_mpi_not_grown(&x); + ret &= t_compare_x32(0x19700101, x.words[0]); + ret &= t_compare_u(29, x.msb); + ret &= t_compare_u(1, x.neg); + return (ret); +} + +/* + * Copy an MPI into itself + */ +static int +t_mpi_copy_same(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + cryb_mpi x = { }; + int ret = 1; + + /* how do you really test this? oh well */ + mpi_set(&x, -0x19700101); + assert(x.words[0] == 0x19700101 && x.msb == 29 && x.neg == 1); + mpi_copy(&x, &x); + ret &= t_mpi_not_grown(&x); + ret &= t_compare_x32(0x19700101, x.words[0]); + ret &= t_compare_u(29, x.msb); + ret &= t_compare_u(1, x.neg); + return (ret); +} + +/* + * Copy a static MPI into another + */ +static int +t_mpi_copy_static(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + cryb_mpi x = { }, y = { }; + int ret = 1; + + mpi_set(&x, -0x19700101); + assert(x.words[0] == 0x19700101 && x.msb == 29 && x.neg == 1); + mpi_copy(&y, &x); + ret &= t_mpi_not_grown(&y); + ret &= t_compare_x32(0x19700101, y.words[0]); + ret &= t_compare_u(29, y.msb); + ret &= t_compare_u(1, y.neg); + return (ret); +} + +/* + * Copy a force-grown MPI + */ +static int +t_mpi_copy_grown(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + cryb_mpi x = { }, y = { }; + int ret = 1; + + mpi_set(&x, -0x19700101); + assert(x.words[0] == 0x19700101 && x.msb == 29 && x.neg == 1); + /* the original is larger than necessary */ + mpi_grow(&x, CRYB_MPI_SWORDS * 32 + 1); + assert(x.words != x.swords && x.size > CRYB_MPI_SWORDS); + mpi_copy(&y, &x); + /* the copy is just large enough to fit the actual value */ + ret &= t_mpi_not_grown(&y); + ret &= t_compare_x32(0x19700101, y.words[0]); + ret &= t_compare_u(29, y.msb); + ret &= t_compare_u(1, y.neg); + return (ret); +} + +/* + * Copy an organically-grown MPI + */ +static int +t_mpi_copy_long(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + cryb_mpi x = { }, y = { }; + int ret = 1; + + mpi_load(&x, large_v, sizeof large_v); + assert(x.words != x.swords && x.size > CRYB_MPI_SWORDS && + memcmp(x.words, large_e, sizeof large_e) == 0); + mpi_copy(&y, &x); + ret &= t_mpi_grown(&y); + ret &= t_compare_mem(x.words, y.words, (x.msb + 7) / 8); + ret &= t_compare_u(x.msb, y.msb); + ret &= t_compare_u(x.neg, y.neg); + return (ret); +} + +/* + * Swap two values (not grown) + */ +static int +t_mpi_swap_static(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + cryb_mpi x = { }, y = { }; + int ret = 1; + + mpi_set(&x, -0x19700101); + assert(x.words[0] == 0x19700101 && x.msb == 29 && x.neg == 1); + mpi_set(&y, 0x20140901); + assert(y.words[0] == 0x20140901 && y.msb == 30 && y.neg == 0); + mpi_swap(&x, &y); + ret &= t_compare_x32(0x20140901, x.words[0]); + ret &= t_compare_u(30, x.msb); + ret &= t_compare_u(0, x.neg); + ret &= t_compare_x32(0x19700101, y.words[0]); + ret &= t_compare_u(29, y.msb); + ret &= t_compare_u(1, y.neg); + return (ret); +} + +/* + * Swap two values (grown) + */ +static int +t_mpi_swap_grown(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + cryb_mpi x = { }, y = { }; + int ret = 1; + + mpi_set(&x, -0x19700101); + mpi_grow(&x, CRYB_MPI_SWORDS * 32 + 1); + assert(x.words != x.swords && x.words[0] == 0x19700101 && + x.msb == 29 && x.neg == 1); + mpi_set(&y, 0x20140901); + mpi_grow(&y, CRYB_MPI_SWORDS * 32 + 1); + assert(y.words != y.swords && y.words[0] == 0x20140901 && + y.msb == 30 && y.neg == 0); + mpi_swap(&x, &y); + ret &= t_compare_x32(0x20140901, x.words[0]); + ret &= t_compare_u(30, x.msb); + ret &= t_compare_u(0, x.neg); + ret &= t_compare_x32(0x19700101, y.words[0]); + ret &= t_compare_u(29, y.msb); + ret &= t_compare_u(1, y.neg); + return (ret); +} + + +/*************************************************************************** + * Load / store + */ + +static struct t_load_case { + const char *desc; + uint8_t v[16]; + size_t vlen; + uint32_t e[4]; + unsigned int msb; +} t_load_cases[] = { + { + "load nothing", + { }, 0, + { 0x00000000, }, 0, + }, + { + "load 0x00", + { 0x00, }, 1, + { 0x00000000, }, 0, + }, + { + "load 0x01", + { 0x01, }, 1, + { 0x00000001, }, 1, + }, + { + "load 0x0102", + { 0x01, 0x02, }, 2, + { 0x00000102, }, 9, + }, + { + "load 0x010203", + { 0x01, 0x02, 0x03, }, 3, + { 0x00010203, }, 17, + }, + { + "load 0x01020304", + { 0x01, 0x02, 0x03, 0x04, }, 4, + { 0x01020304, }, 25, + }, + { + "load 0x0102030405", + { 0x01, 0x02, 0x03, 0x04, 0x05, }, 5, + { 0x02030405, 0x00000001, }, 33, + }, + { + "load 0x010203040506", + { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, }, 6, + { 0x03040506, 0x00000102, }, 41, + }, + { + "load 0x01020304050607", + { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, }, 7, + { 0x04050607, 0x00010203, }, 49, + }, + { + "load 0x0102030405060708", + { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, }, 8, + { 0x05060708, 0x01020304, }, 57, + }, +}; + +/* + * Load a string of bytes, verify result. + */ +static int +t_mpi_load(char **desc CRYB_UNUSED, void *arg) +{ + struct t_load_case *tc = arg; + cryb_mpi x = { }; + int ret = 1; + + mpi_load(&x, tc->v, tc->vlen); + ret &= t_compare_mem(tc->e, x.words, sizeof tc->e); + ret &= t_compare_u(tc->msb, x.msb); + return (ret); +} + +/* + * As above, but with a large number to force reallocation. + */ +static int +t_mpi_large_load(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + cryb_mpi x = { }; + int ret = 1; + + mpi_load(&x, large_v, sizeof large_v); + if (x.words == x.swords) { + t_verbose("reallocation failed to occur"); + return (0); + } + ret &= t_compare_mem(large_e, x.words, sizeof large_e); + ret &= t_compare_u(256 * 8, x.msb); + return (ret); +} + + +/*************************************************************************** + * Left / right shift + */ + +static struct t_lsh_case { + const char *desc; + uint8_t v[16]; + size_t vlen; + unsigned int n; + uint8_t e[16]; + size_t elen; +} t_lsh_cases[] = { + { + "0x00 << 0 == 0x00", + { 0x00, }, 1, + 0, + { 0x00, }, 1, + }, + { + "0x00 << 1 == 0x00", + { 0x00, }, 1, + 1, + { 0x00, }, 1, + }, + { + "0x01 << 0 == 0x01", + { 0x01, }, 1, + 0, + { 0x01, }, 1, + }, + { + "0x01 << 1 == 0x02", + { 0x01, }, 1, + 1, + { 0x02, }, 1, + }, + { + "0x11 << 1 == 0x22", + { 0x11, }, 1, + 1, + { 0x22, }, 1, + }, + { + "0x11 << 32 == 0x1100000000", + { 0x00, 0x00, 0x00, 0x11, }, 4, + 32, + { 0x11, 0x00, 0x00, 0x00, 0x00, }, 5, + }, + { + "0x22 << 31 == 0x1100000000", + { 0x00, 0x00, 0x00, 0x22, }, 4, + 31, + { 0x11, 0x00, 0x00, 0x00, 0x00, }, 5, + }, + { + "0x80000000 << 1 == 0x100000000", + { 0x80, 0x00, 0x00, 0x00, }, 4, + 1, + { 0x01, 0x00, 0x00, 0x00, 0x00, }, 5, + }, +}; + +/* + * Load a number, left-shift it and verify the result. + */ +static int +t_mpi_lsh(char **desc CRYB_UNUSED, void *arg) +{ + struct t_lsh_case *tc = arg; + cryb_mpi x = { }, e = { }; + + mpi_load(&x, tc->v, tc->vlen); + mpi_load(&e, tc->e, tc->elen); + mpi_lshift(&x, tc->n); + return (t_compare_mem(e.swords, x.swords, sizeof e.swords)); +} + +/* + * As above, but with an initial number large enough to cause an overflow + * and reallocation. + */ +static int +t_mpi_large_lsh(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + cryb_mpi x = { }; + int ret = 1; + + mpi_load(&x, large_v, sizeof large_v); + mpi_lshift(&x, 32); + if (x.size < LARGE_E_SIZE + 1) { + t_verbose("reallocation failed to occur"); + return (0); + } + ret &= t_compare_mem(t_zero, x.words, sizeof x.words[0]); + ret &= t_compare_mem(large_e, x.words + 1, sizeof large_e); + ret &= t_compare_u(256 * 8 + 32, x.msb); + return (ret); +} + +static struct t_rsh_case { + const char *desc; + uint8_t v[16]; + size_t vlen; + unsigned int n; + uint8_t e[16]; + size_t elen; +} t_rsh_cases[] = { + { + "0x00 >> 0 == 0x00", + { 0x00, }, 1, + 0, + { 0x00, }, 1, + }, + { + "0x00 >> 1 == 0x00", + { 0x00, }, 1, + 1, + { 0x00, }, 1, + }, + { + "0x01 >> 1 == 0x00", + { 0x01, }, 1, + 1, + { 0x00, }, 1, + }, + { + "0x02 >> 1 == 0x01", + { 0x02, }, 1, + 1, + { 0x01, }, 1, + }, + { + "0x22 >> 1 == 0x11", + { 0x22, }, 1, + 1, + { 0x11, }, 1, + }, + { + "0x1100000000 >> 32 == 0x11", + { 0x11, 0x00, 0x00, 0x00, 0x00, }, 5, + 32, + { 0x00, 0x00, 0x00, 0x11, }, 4, + }, + { + "0x1100000000 >> 31 == 0x22", + { 0x11, 0x00, 0x00, 0x00, 0x00, }, 5, + 31, + { 0x00, 0x00, 0x00, 0x22, }, 4, + }, + { + "0x100000000 >> 1 == 0x80000000", + { 0x01, 0x00, 0x00, 0x00, 0x00, }, 5, + 1, + { 0x80, 0x00, 0x00, 0x00, }, 4, + }, +}; + +/* + * Load a number, right-shift it and verify the result. + */ +static int +t_mpi_rsh(char **desc CRYB_UNUSED, void *arg) +{ + struct t_rsh_case *tc = arg; + cryb_mpi x = { }, e = { }; + + mpi_load(&x, tc->v, tc->vlen); + mpi_load(&e, tc->e, tc->elen); + mpi_rshift(&x, tc->n); + return (t_compare_mem(e.swords, x.swords, sizeof e.swords)); +} + + +/*************************************************************************** + * Addition / subtraction + */ + +static struct t_add_case { + const char *desc; + uint8_t a[16]; + size_t amsb; + uint8_t b[16]; + size_t bmsb; + uint8_t e[16]; + size_t emsb; +} t_add_cases[] = { + { + "0 + 0 == 0", + { }, 0, + { }, 0, + { }, 0, + }, + { + "0 + 1 == 1", + { }, 0, + { 0x01, }, 1, + { 0x01, }, 1, + }, + { + "1 + 0 == 1", + { 0x01, }, 1, + { }, 0, + { 0x01, }, 1, + }, + { + "2 + 2 == 4", + { 0x02, }, 2, + { 0x02, }, 2, + { 0x04, }, 3, + }, + { + /* simple carry */ + "0xffffffff + 0x01 = 0x0100000000", + { 0xff, 0xff, 0xff, 0xff, }, 32, + { 0x01, }, 1, + { 0x01, 0x00, 0x00, 0x00, 0x00, }, 33, + }, + { + /* complex carry */ + "0xffffffffffffffff + 0x0100000001 = 0x010000000100000000", + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }, 64, + { 0x01, 0x00, 0x00, 0x00, 0x01, }, 33, + { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, }, 65, + }, +}; + +static int +t_mpi_add(char **desc CRYB_UNUSED, void *arg) +{ + struct t_add_case *tc = arg; + cryb_mpi a = { }, b = { }, e = { }, x = { }; + int ret = 1; + + mpi_load(&a, tc->a, (tc->amsb + 7) / 8); + mpi_load(&b, tc->b, (tc->bmsb + 7) / 8); + mpi_load(&e, tc->e, (tc->emsb + 7) / 8); + mpi_add_abs(&x, &a, &b); + ret &= t_compare_mem(e.swords, x.swords, CRYB_MPI_SWORDS); + ret &= t_compare_u(e.msb, x.msb); + ret &= t_compare_u(e.neg, x.neg); + return (ret); +} + +/* + * Target is the first operand + */ +static int +t_mpi_add_b_to_a(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + cryb_mpi a = { }, b = { }, e = { }; + int ret = 1; + + mpi_set(&a, 0x19700101); + mpi_set(&b, 0x20140901); + mpi_set(&e, 0x19700101 + 0x20140901); + mpi_add_abs(&a, &a, &b); + ret &= t_compare_mem(e.swords, a.swords, CRYB_MPI_SWORDS); + ret &= t_compare_u(e.msb, a.msb); + ret &= t_compare_u(e.neg, a.neg); + return (ret); +} + +/* + * Target is the second operand + */ +static int +t_mpi_add_a_to_b(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + cryb_mpi a = { }, b = { }, e = { }; + int ret = 1; + + mpi_set(&a, 0x19700101); + mpi_set(&b, 0x20140901); + mpi_set(&e, 0x19700101 + 0x20140901); + mpi_add_abs(&b, &a, &b); + ret &= t_compare_mem(e.swords, b.swords, CRYB_MPI_SWORDS); + ret &= t_compare_u(e.msb, b.msb); + ret &= t_compare_u(e.neg, b.neg); + return (ret); +} + + +/*************************************************************************** + * Boilerplate + */ + +int +t_prepare(int argc, char *argv[]) +{ + unsigned int i; + + (void)argc; + (void)argv; + + /* initialize constants used in multiple test cases */ + mpi_zero(&z); /* circular... */ + for (i = 0; i < 256; ++i) + large_v[i] = ~i; + for (i = 0; i < 64; ++i) + large_e[63 - i] = + large_v[i * 4] << 24 | large_v[i * 4 + 1] << 16 | + large_v[i * 4 + 2] << 8 | large_v[i * 4 + 3]; + + /* basic tests */ + t_add_test(t_mpi_init, NULL, "init"); + t_add_test(t_mpi_set_zero, NULL, "set to 0"); + t_add_test(t_mpi_fast_init, NULL, "fast init"); + t_add_test(t_mpi_grow_ok, NULL, "grow (success)"); + t_add_test(t_mpi_grow_fail, NULL, "grow (failure)"); + t_add_test(t_mpi_grow_twice, NULL, "grow (twice)"); + t_add_test(t_mpi_destroy_uninit, NULL, "destroy static"); + t_add_test(t_mpi_destroy_static, NULL, "destroy static"); + t_add_test(t_mpi_destroy_grown, NULL, "destroy grown"); + + /* assignment, copying, negation, swapping */ + t_add_test(t_mpi_set_positive, NULL, "set to positive value"); + t_add_test(t_mpi_set_negative, NULL, "set to negative value"); + t_add_test(t_mpi_negate_zero, NULL, "negate zero"); + t_add_test(t_mpi_negate_nonzero, NULL, "negate nonzero"); + t_add_test(t_mpi_copy_same, NULL, "copy (same)"); + t_add_test(t_mpi_copy_static, NULL, "copy (static)"); + t_add_test(t_mpi_copy_grown, NULL, "copy (grown)"); + t_add_test(t_mpi_copy_long, NULL, "copy (long)"); + t_add_test(t_mpi_swap_static, NULL, "swap (static)"); + t_add_test(t_mpi_swap_grown, NULL, "swap (grown)"); + + for (i = 0; i < sizeof t_load_cases / sizeof t_load_cases[0]; ++i) + t_add_test(t_mpi_load, &t_load_cases[i], t_load_cases[i].desc); + t_add_test(t_mpi_large_load, NULL, "large load"); + for (i = 0; i < sizeof t_lsh_cases / sizeof t_lsh_cases[0]; ++i) + t_add_test(t_mpi_lsh, &t_lsh_cases[i], t_lsh_cases[i].desc); + t_add_test(t_mpi_large_lsh, NULL, "large left shift"); + for (i = 0; i < sizeof t_rsh_cases / sizeof t_rsh_cases[0]; ++i) + t_add_test(t_mpi_rsh, &t_rsh_cases[i], t_rsh_cases[i].desc); + for (i = 0; i < sizeof t_add_cases / sizeof t_add_cases[0]; ++i) + t_add_test(t_mpi_add, &t_add_cases[i], t_add_cases[i].desc); + t_add_test(t_mpi_add_a_to_b, NULL, "add into first operand"); + t_add_test(t_mpi_add_b_to_a, NULL, "add into second operand"); + return (0); +} + +void +t_cleanup(void) +{ +}