xref: /onnv-gate/usr/src/common/bignum/bignumimpl.c (revision 12719:bd9fb35d09c2)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
56557Sfr41279  * Common Development and Distribution License (the "License").
66557Sfr41279  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
2112573SDina.Nimeh@Sun.COM 
220Sstevel@tonic-gate /*
2312573SDina.Nimeh@Sun.COM  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate /*
270Sstevel@tonic-gate  * Configuration guide
280Sstevel@tonic-gate  * -------------------
290Sstevel@tonic-gate  *
300Sstevel@tonic-gate  * There are 4 preprocessor symbols used to configure the bignum
310Sstevel@tonic-gate  * implementation.  This file contains no logic to configure based on
320Sstevel@tonic-gate  * processor; we leave that to the Makefiles to specify.
330Sstevel@tonic-gate  *
340Sstevel@tonic-gate  * USE_FLOATING_POINT
350Sstevel@tonic-gate  *   Meaning: There is support for a fast floating-point implementation of
360Sstevel@tonic-gate  *   Montgomery multiply.
370Sstevel@tonic-gate  *
380Sstevel@tonic-gate  * PSR_MUL
390Sstevel@tonic-gate  *   Meaning: There are processor-specific versions of the low level
400Sstevel@tonic-gate  *   functions to implement big_mul.  Those functions are: big_mul_set_vec,
410Sstevel@tonic-gate  *   big_mul_add_vec, big_mul_vec, and big_sqr_vec.  PSR_MUL implies support
420Sstevel@tonic-gate  *   for all 4 functions.  You cannot pick and choose which subset of these
430Sstevel@tonic-gate  *   functions to support; that would lead to a rat's nest of #ifdefs.
440Sstevel@tonic-gate  *
450Sstevel@tonic-gate  * HWCAP
460Sstevel@tonic-gate  *   Meaning: Call multiply support functions through a function pointer.
478933Sopensolaris@drydog.com  *   On x86, there are multiple implementations for different hardware
480Sstevel@tonic-gate  *   capabilities, such as MMX, SSE2, etc.  Tests are made at run-time, when
490Sstevel@tonic-gate  *   a function is first used.  So, the support functions are called through
500Sstevel@tonic-gate  *   a function pointer.  There is no need for that on Sparc, because there
510Sstevel@tonic-gate  *   is only one implementation; support functions are called directly.
520Sstevel@tonic-gate  *   Later, if there were some new VIS instruction, or something, and a
530Sstevel@tonic-gate  *   run-time test were needed, rather than variant kernel modules and
540Sstevel@tonic-gate  *   libraries, then HWCAP would be defined for Sparc, as well.
550Sstevel@tonic-gate  *
560Sstevel@tonic-gate  * UMUL64
570Sstevel@tonic-gate  *   Meaning: It is safe to use generic C code that assumes the existence
580Sstevel@tonic-gate  *   of a 32 x 32 --> 64 bit unsigned multiply.  If this is not defined,
590Sstevel@tonic-gate  *   then the generic code for big_mul_add_vec() must necessarily be very slow,
600Sstevel@tonic-gate  *   because it must fall back to using 16 x 16 --> 32 bit multiplication.
610Sstevel@tonic-gate  *
620Sstevel@tonic-gate  */
630Sstevel@tonic-gate 
640Sstevel@tonic-gate 
658933Sopensolaris@drydog.com #include <sys/types.h>
668933Sopensolaris@drydog.com #include "bignum.h"
678933Sopensolaris@drydog.com 
680Sstevel@tonic-gate #ifdef	_KERNEL
696557Sfr41279 #include <sys/ddi.h>
706557Sfr41279 #include <sys/mdesc.h>
716557Sfr41279 #include <sys/crypto/common.h>
720Sstevel@tonic-gate 
730Sstevel@tonic-gate #include <sys/kmem.h>
740Sstevel@tonic-gate #include <sys/param.h>
750Sstevel@tonic-gate #include <sys/sunddi.h>
760Sstevel@tonic-gate 
778933Sopensolaris@drydog.com #else
788933Sopensolaris@drydog.com #include <stdlib.h>
798933Sopensolaris@drydog.com #include <stdio.h>
808933Sopensolaris@drydog.com #include <assert.h>
818933Sopensolaris@drydog.com #define	ASSERT	assert
828933Sopensolaris@drydog.com #endif	/* _KERNEL */
838933Sopensolaris@drydog.com 
849302Sopensolaris@drydog.com #ifdef __amd64
859302Sopensolaris@drydog.com #ifdef _KERNEL
869302Sopensolaris@drydog.com #include <sys/x86_archext.h>	/* cpuid_getvendor() */
879302Sopensolaris@drydog.com #include <sys/cpuvar.h>
889302Sopensolaris@drydog.com #else
899302Sopensolaris@drydog.com #include <sys/auxv.h>		/* getisax() */
909302Sopensolaris@drydog.com #endif  /* _KERNEL */
919302Sopensolaris@drydog.com #endif  /* __amd64 */
929302Sopensolaris@drydog.com 
939302Sopensolaris@drydog.com 
948933Sopensolaris@drydog.com #ifdef	_LP64 /* truncate 64-bit size_t to 32-bits */
958933Sopensolaris@drydog.com #define	UI32(ui)	((uint32_t)ui)
968933Sopensolaris@drydog.com #else /* size_t already 32-bits */
978933Sopensolaris@drydog.com #define	UI32(ui)	(ui)
988933Sopensolaris@drydog.com #endif
998933Sopensolaris@drydog.com 
1008933Sopensolaris@drydog.com 
1018933Sopensolaris@drydog.com #ifdef	_KERNEL
1020Sstevel@tonic-gate #define	big_malloc(size)	kmem_alloc(size, KM_NOSLEEP)
1030Sstevel@tonic-gate #define	big_free(ptr, size)	kmem_free(ptr, size)
1040Sstevel@tonic-gate 
1059302Sopensolaris@drydog.com /*
1069302Sopensolaris@drydog.com  * big_realloc()
1079302Sopensolaris@drydog.com  * Allocate memory of newsize bytes and copy oldsize bytes
1089302Sopensolaris@drydog.com  * to the newly-allocated memory, then free the
1099302Sopensolaris@drydog.com  * previously-allocated memory.
1109302Sopensolaris@drydog.com  * Note: newsize must be > oldsize
1119302Sopensolaris@drydog.com  */
1120Sstevel@tonic-gate void *
big_realloc(void * from,size_t oldsize,size_t newsize)1130Sstevel@tonic-gate big_realloc(void *from, size_t oldsize, size_t newsize)
1140Sstevel@tonic-gate {
1150Sstevel@tonic-gate 	void *rv;
1160Sstevel@tonic-gate 
1170Sstevel@tonic-gate 	rv = kmem_alloc(newsize, KM_NOSLEEP);
1180Sstevel@tonic-gate 	if (rv != NULL)
1190Sstevel@tonic-gate 		bcopy(from, rv, oldsize);
1200Sstevel@tonic-gate 	kmem_free(from, oldsize);
1210Sstevel@tonic-gate 	return (rv);
1220Sstevel@tonic-gate }
1230Sstevel@tonic-gate 
1240Sstevel@tonic-gate #else	/* _KERNEL */
1250Sstevel@tonic-gate 
1260Sstevel@tonic-gate #ifndef MALLOC_DEBUG
1270Sstevel@tonic-gate 
1280Sstevel@tonic-gate #define	big_malloc(size)	malloc(size)
1290Sstevel@tonic-gate #define	big_free(ptr, size)	free(ptr)
1300Sstevel@tonic-gate 
1310Sstevel@tonic-gate #else
1320Sstevel@tonic-gate 
1330Sstevel@tonic-gate void
big_free(void * ptr,size_t size)1340Sstevel@tonic-gate big_free(void *ptr, size_t size)
1350Sstevel@tonic-gate {
1360Sstevel@tonic-gate 	printf("freed %d bytes at %p\n", size, ptr);
1370Sstevel@tonic-gate 	free(ptr);
1380Sstevel@tonic-gate }
1390Sstevel@tonic-gate 
1400Sstevel@tonic-gate void *
big_malloc(size_t size)1410Sstevel@tonic-gate big_malloc(size_t size)
1420Sstevel@tonic-gate {
1430Sstevel@tonic-gate 	void *rv;
1440Sstevel@tonic-gate 	rv = malloc(size);
1450Sstevel@tonic-gate 	printf("malloced %d bytes, addr:%p\n", size, rv);
1460Sstevel@tonic-gate 	return (rv);
1470Sstevel@tonic-gate }
1480Sstevel@tonic-gate #endif /* MALLOC_DEBUG */
1490Sstevel@tonic-gate 
1500Sstevel@tonic-gate #define	big_realloc(x, y, z) realloc((x), (z))
1510Sstevel@tonic-gate 
1528933Sopensolaris@drydog.com 
1538933Sopensolaris@drydog.com /*
1548933Sopensolaris@drydog.com  * printbignum()
1558933Sopensolaris@drydog.com  * Print a BIGNUM type to stdout.
1568933Sopensolaris@drydog.com  */
1570Sstevel@tonic-gate void
printbignum(char * aname,BIGNUM * a)1580Sstevel@tonic-gate printbignum(char *aname, BIGNUM *a)
1590Sstevel@tonic-gate {
1600Sstevel@tonic-gate 	int i;
1610Sstevel@tonic-gate 
1620Sstevel@tonic-gate 	(void) printf("\n%s\n%d\n", aname, a->sign*a->len);
1630Sstevel@tonic-gate 	for (i = a->len - 1; i >= 0; i--) {
1646557Sfr41279 #ifdef BIGNUM_CHUNK_32
1650Sstevel@tonic-gate 		(void) printf("%08x ", a->value[i]);
1668933Sopensolaris@drydog.com 		if (((i & (BITSINBYTE - 1)) == 0) && (i != 0)) {
1676557Sfr41279 			(void) printf("\n");
1686557Sfr41279 		}
1696557Sfr41279 #else
1706557Sfr41279 		(void) printf("%08x %08x ", (uint32_t)((a->value[i]) >> 32),
1716557Sfr41279 		    (uint32_t)((a->value[i]) & 0xffffffff));
1728933Sopensolaris@drydog.com 		if (((i & 3) == 0) && (i != 0)) { /* end of this chunk */
1736557Sfr41279 			(void) printf("\n");
1746557Sfr41279 		}
1756557Sfr41279 #endif
1760Sstevel@tonic-gate 	}
1770Sstevel@tonic-gate 	(void) printf("\n");
1780Sstevel@tonic-gate }
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate #endif	/* _KERNEL */
1810Sstevel@tonic-gate 
1820Sstevel@tonic-gate 
1839302Sopensolaris@drydog.com #ifdef  __amd64
1849302Sopensolaris@drydog.com /*
1859302Sopensolaris@drydog.com  * Return 1 if executing on Intel, otherwise 0 (e.g., AMD64).
18611141Sopensolaris@drydog.com  * Cache the result, as the CPU can't change.
18711141Sopensolaris@drydog.com  *
18811141Sopensolaris@drydog.com  * Note: the userland version uses getisax() and checks for an AMD-64-only
18911141Sopensolaris@drydog.com  * feature.  The kernel version uses cpuid_getvendor().
1909302Sopensolaris@drydog.com  */
1919302Sopensolaris@drydog.com static int
bignum_on_intel(void)1929302Sopensolaris@drydog.com bignum_on_intel(void)
1939302Sopensolaris@drydog.com {
19411141Sopensolaris@drydog.com 	static int	cached_result = -1;
19511141Sopensolaris@drydog.com 
19611141Sopensolaris@drydog.com 	if (cached_result == -1) { /* first time */
1979302Sopensolaris@drydog.com #ifdef _KERNEL
19811141Sopensolaris@drydog.com 		cached_result = (cpuid_getvendor(CPU) == X86_VENDOR_Intel);
1999302Sopensolaris@drydog.com #else
20011141Sopensolaris@drydog.com 		uint_t  ui;
20111141Sopensolaris@drydog.com 
20211141Sopensolaris@drydog.com 		(void) getisax(&ui, 1);
20311141Sopensolaris@drydog.com 		cached_result = ((ui & AV_386_AMD_MMX) == 0);
2049302Sopensolaris@drydog.com #endif  /* _KERNEL */
20511141Sopensolaris@drydog.com 	}
20611141Sopensolaris@drydog.com 
20711141Sopensolaris@drydog.com 	return (cached_result);
2089302Sopensolaris@drydog.com }
2099302Sopensolaris@drydog.com #endif  /* __amd64 */
2109302Sopensolaris@drydog.com 
2119302Sopensolaris@drydog.com 
2128933Sopensolaris@drydog.com /*
2138933Sopensolaris@drydog.com  * big_init()
2148933Sopensolaris@drydog.com  * Initialize and allocate memory for a BIGNUM type.
2158933Sopensolaris@drydog.com  *
2168933Sopensolaris@drydog.com  * big_init(number, size) is equivalent to big_init1(number, size, NULL, 0)
2178933Sopensolaris@drydog.com  *
2188933Sopensolaris@drydog.com  * Note: call big_finish() to free memory allocated by big_init().
2198933Sopensolaris@drydog.com  *
2208933Sopensolaris@drydog.com  * Input:
2218933Sopensolaris@drydog.com  * number	Uninitialized memory for BIGNUM
2228933Sopensolaris@drydog.com  * size		Minimum size, in BIG_CHUNK_SIZE-bit words, required for BIGNUM
2238933Sopensolaris@drydog.com  *
2248933Sopensolaris@drydog.com  * Output:
2258933Sopensolaris@drydog.com  * number	Initialized BIGNUM
2268933Sopensolaris@drydog.com  *
2278933Sopensolaris@drydog.com  * Return BIG_OK on success or BIG_NO_MEM for an allocation error.
2288933Sopensolaris@drydog.com  */
2290Sstevel@tonic-gate BIG_ERR_CODE
big_init(BIGNUM * number,int size)2300Sstevel@tonic-gate big_init(BIGNUM *number, int size)
2310Sstevel@tonic-gate {
2328933Sopensolaris@drydog.com 	number->value = big_malloc(BIGNUM_WORDSIZE * size);
2330Sstevel@tonic-gate 	if (number->value == NULL) {
2340Sstevel@tonic-gate 		return (BIG_NO_MEM);
2350Sstevel@tonic-gate 	}
2360Sstevel@tonic-gate 	number->size = size;
2370Sstevel@tonic-gate 	number->len = 0;
2380Sstevel@tonic-gate 	number->sign = 1;
2390Sstevel@tonic-gate 	number->malloced = 1;
2400Sstevel@tonic-gate 	return (BIG_OK);
2410Sstevel@tonic-gate }
2420Sstevel@tonic-gate 
2438933Sopensolaris@drydog.com 
2448933Sopensolaris@drydog.com /*
2458933Sopensolaris@drydog.com  * big_init1()
2468933Sopensolaris@drydog.com  * Initialize and, if needed, allocate memory for a BIGNUM type.
2478933Sopensolaris@drydog.com  * Use the buffer passed, buf, if any, instad of allocating memory
2488933Sopensolaris@drydog.com  * if it's at least "size" bytes.
2498933Sopensolaris@drydog.com  *
2508933Sopensolaris@drydog.com  * Note: call big_finish() to free memory allocated by big_init().
2518933Sopensolaris@drydog.com  *
2528933Sopensolaris@drydog.com  * Input:
2538933Sopensolaris@drydog.com  * number	Uninitialized memory for BIGNUM
2548933Sopensolaris@drydog.com  * size		Minimum size, in BIG_CHUNK_SIZE-bit words, required for BIGNUM
2558933Sopensolaris@drydog.com  * buf		Buffer for storing a BIGNUM.
2568933Sopensolaris@drydog.com  *		If NULL, big_init1() will allocate a buffer
2578933Sopensolaris@drydog.com  * bufsize	Size, in BIG_CHUNK_SIZE_bit words, of buf
2588933Sopensolaris@drydog.com  *
2598933Sopensolaris@drydog.com  * Output:
2608933Sopensolaris@drydog.com  * number	Initialized BIGNUM
2618933Sopensolaris@drydog.com  *
2628933Sopensolaris@drydog.com  * Return BIG_OK on success or BIG_NO_MEM for an allocation error.
2638933Sopensolaris@drydog.com  */
2640Sstevel@tonic-gate BIG_ERR_CODE
big_init1(BIGNUM * number,int size,BIG_CHUNK_TYPE * buf,int bufsize)2656557Sfr41279 big_init1(BIGNUM *number, int size, BIG_CHUNK_TYPE *buf, int bufsize)
2660Sstevel@tonic-gate {
2670Sstevel@tonic-gate 	if ((buf == NULL) || (size > bufsize)) {
2688933Sopensolaris@drydog.com 		number->value = big_malloc(BIGNUM_WORDSIZE * size);
2690Sstevel@tonic-gate 		if (number->value == NULL) {
2700Sstevel@tonic-gate 			return (BIG_NO_MEM);
2710Sstevel@tonic-gate 		}
2720Sstevel@tonic-gate 		number->size = size;
2730Sstevel@tonic-gate 		number->malloced = 1;
2740Sstevel@tonic-gate 	} else {
2750Sstevel@tonic-gate 		number->value = buf;
2760Sstevel@tonic-gate 		number->size = bufsize;
2770Sstevel@tonic-gate 		number->malloced = 0;
2780Sstevel@tonic-gate 	}
2798933Sopensolaris@drydog.com 	number->len = 0;
2808933Sopensolaris@drydog.com 	number->sign = 1;
2810Sstevel@tonic-gate 
2820Sstevel@tonic-gate 	return (BIG_OK);
2830Sstevel@tonic-gate }
2840Sstevel@tonic-gate 
2858933Sopensolaris@drydog.com 
2868933Sopensolaris@drydog.com /*
2878933Sopensolaris@drydog.com  * big_finish()
2888933Sopensolaris@drydog.com  * Free memory, if any, allocated by big_init() or big_init1().
2898933Sopensolaris@drydog.com  */
2900Sstevel@tonic-gate void
big_finish(BIGNUM * number)2910Sstevel@tonic-gate big_finish(BIGNUM *number)
2920Sstevel@tonic-gate {
2930Sstevel@tonic-gate 	if (number->malloced == 1) {
2948933Sopensolaris@drydog.com 		big_free(number->value, BIGNUM_WORDSIZE * number->size);
2950Sstevel@tonic-gate 		number->malloced = 0;
2960Sstevel@tonic-gate 	}
2970Sstevel@tonic-gate }
2980Sstevel@tonic-gate 
2996557Sfr41279 
3000Sstevel@tonic-gate /*
3018933Sopensolaris@drydog.com  * bn->size should be at least
3028933Sopensolaris@drydog.com  * (len + BIGNUM_WORDSIZE - 1) / BIGNUM_WORDSIZE bytes
3030Sstevel@tonic-gate  * converts from byte-big-endian format to bignum format (words in
3040Sstevel@tonic-gate  * little endian order, but bytes within the words big endian)
3050Sstevel@tonic-gate  */
3060Sstevel@tonic-gate void
bytestring2bignum(BIGNUM * bn,uchar_t * kn,size_t len)3070Sstevel@tonic-gate bytestring2bignum(BIGNUM *bn, uchar_t *kn, size_t len)
3080Sstevel@tonic-gate {
3098933Sopensolaris@drydog.com 	int		i, j;
3108933Sopensolaris@drydog.com 	uint32_t	offs;
3118933Sopensolaris@drydog.com 	const uint32_t	slen = UI32(len);
3126557Sfr41279 	BIG_CHUNK_TYPE	word;
3130Sstevel@tonic-gate 	uchar_t		*knwordp;
3140Sstevel@tonic-gate 
3158933Sopensolaris@drydog.com 	if (slen == 0) {
3168933Sopensolaris@drydog.com 		bn->len = 1;
3178933Sopensolaris@drydog.com 		bn->value[0] = 0;
3188933Sopensolaris@drydog.com 		return;
3198933Sopensolaris@drydog.com 	}
3208933Sopensolaris@drydog.com 
3218933Sopensolaris@drydog.com 	offs = slen % BIGNUM_WORDSIZE;
3228933Sopensolaris@drydog.com 	bn->len = slen / BIGNUM_WORDSIZE;
3238933Sopensolaris@drydog.com 
3248933Sopensolaris@drydog.com 	for (i = 0; i < slen / BIGNUM_WORDSIZE; i++) {
3258933Sopensolaris@drydog.com 		knwordp = &(kn[slen - BIGNUM_WORDSIZE * (i + 1)]);
3260Sstevel@tonic-gate 		word = knwordp[0];
3278933Sopensolaris@drydog.com 		for (j = 1; j < BIGNUM_WORDSIZE; j++) {
3288933Sopensolaris@drydog.com 			word = (word << BITSINBYTE) + knwordp[j];
3290Sstevel@tonic-gate 		}
3300Sstevel@tonic-gate 		bn->value[i] = word;
3310Sstevel@tonic-gate 	}
3320Sstevel@tonic-gate 	if (offs > 0) {
3330Sstevel@tonic-gate 		word = kn[0];
3348933Sopensolaris@drydog.com 		for (i = 1; i < offs; i++) word = (word << BITSINBYTE) + kn[i];
3350Sstevel@tonic-gate 		bn->value[bn->len++] = word;
3360Sstevel@tonic-gate 	}
3378933Sopensolaris@drydog.com 	while ((bn->len > 1) && (bn->value[bn->len - 1] == 0)) {
3380Sstevel@tonic-gate 		bn->len --;
3390Sstevel@tonic-gate 	}
3400Sstevel@tonic-gate }
3410Sstevel@tonic-gate 
3428933Sopensolaris@drydog.com 
3430Sstevel@tonic-gate /*
3440Sstevel@tonic-gate  * copies the least significant len bytes if
3458933Sopensolaris@drydog.com  * len < bn->len * BIGNUM_WORDSIZE
3460Sstevel@tonic-gate  * converts from bignum format to byte-big-endian format.
3476557Sfr41279  * bignum format is words of type  BIG_CHUNK_TYPE in little endian order.
3480Sstevel@tonic-gate  */
3490Sstevel@tonic-gate void
bignum2bytestring(uchar_t * kn,BIGNUM * bn,size_t len)3500Sstevel@tonic-gate bignum2bytestring(uchar_t *kn, BIGNUM *bn, size_t len)
3510Sstevel@tonic-gate {
3528933Sopensolaris@drydog.com 	int		i, j;
3538933Sopensolaris@drydog.com 	uint32_t	offs;
3548933Sopensolaris@drydog.com 	const uint32_t	slen = UI32(len);
3556557Sfr41279 	BIG_CHUNK_TYPE	word;
3566557Sfr41279 
3578933Sopensolaris@drydog.com 	if (len < BIGNUM_WORDSIZE * bn->len) {
3588933Sopensolaris@drydog.com 		for (i = 0; i < slen / BIGNUM_WORDSIZE; i++) {
3590Sstevel@tonic-gate 			word = bn->value[i];
3608933Sopensolaris@drydog.com 			for (j = 0; j < BIGNUM_WORDSIZE; j++) {
3618933Sopensolaris@drydog.com 				kn[slen - BIGNUM_WORDSIZE * i - j - 1] =
3620Sstevel@tonic-gate 				    word & 0xff;
3638933Sopensolaris@drydog.com 				word = word >> BITSINBYTE;
3640Sstevel@tonic-gate 			}
3650Sstevel@tonic-gate 		}
3668933Sopensolaris@drydog.com 		offs = slen % BIGNUM_WORDSIZE;
3670Sstevel@tonic-gate 		if (offs > 0) {
3688933Sopensolaris@drydog.com 			word = bn->value[slen / BIGNUM_WORDSIZE];
3698933Sopensolaris@drydog.com 			for (i =  slen % BIGNUM_WORDSIZE; i > 0; i --) {
3706557Sfr41279 				kn[i - 1] = word & 0xff;
3718933Sopensolaris@drydog.com 				word = word >> BITSINBYTE;
3726557Sfr41279 			}
3730Sstevel@tonic-gate 		}
3740Sstevel@tonic-gate 	} else {
3750Sstevel@tonic-gate 		for (i = 0; i < bn->len; i++) {
3760Sstevel@tonic-gate 			word = bn->value[i];
3778933Sopensolaris@drydog.com 			for (j = 0; j < BIGNUM_WORDSIZE; j++) {
3788933Sopensolaris@drydog.com 				kn[slen - BIGNUM_WORDSIZE * i - j - 1] =
3790Sstevel@tonic-gate 				    word & 0xff;
3808933Sopensolaris@drydog.com 				word = word >> BITSINBYTE;
3810Sstevel@tonic-gate 			}
3820Sstevel@tonic-gate 		}
3838933Sopensolaris@drydog.com 		for (i = 0; i < slen - BIGNUM_WORDSIZE * bn->len; i++) {
3840Sstevel@tonic-gate 			kn[i] = 0;
3850Sstevel@tonic-gate 		}
3860Sstevel@tonic-gate 	}
3870Sstevel@tonic-gate }
3880Sstevel@tonic-gate 
3890Sstevel@tonic-gate 
3900Sstevel@tonic-gate int
big_bitlength(BIGNUM * a)3910Sstevel@tonic-gate big_bitlength(BIGNUM *a)
3920Sstevel@tonic-gate {
3936557Sfr41279 	int		l = 0, b = 0;
3946557Sfr41279 	BIG_CHUNK_TYPE	c;
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate 	l = a->len - 1;
3970Sstevel@tonic-gate 	while ((l > 0) && (a->value[l] == 0)) {
3980Sstevel@tonic-gate 		l--;
3990Sstevel@tonic-gate 	}
4008933Sopensolaris@drydog.com 	b = BIG_CHUNK_SIZE;
4010Sstevel@tonic-gate 	c = a->value[l];
4026557Sfr41279 	while ((b > 1) && ((c & BIG_CHUNK_HIGHBIT) == 0)) {
4030Sstevel@tonic-gate 		c = c << 1;
4040Sstevel@tonic-gate 		b--;
4050Sstevel@tonic-gate 	}
4066557Sfr41279 
4078933Sopensolaris@drydog.com 	return (l * BIG_CHUNK_SIZE + b);
4080Sstevel@tonic-gate }
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate 
4119302Sopensolaris@drydog.com /*
4129302Sopensolaris@drydog.com  * big_copy()
4139302Sopensolaris@drydog.com  * Copy BIGNUM src to dest, allocating memory if needed.
4149302Sopensolaris@drydog.com  */
4150Sstevel@tonic-gate BIG_ERR_CODE
big_copy(BIGNUM * dest,BIGNUM * src)4160Sstevel@tonic-gate big_copy(BIGNUM *dest, BIGNUM *src)
4170Sstevel@tonic-gate {
4186557Sfr41279 	BIG_CHUNK_TYPE	*newptr;
4196557Sfr41279 	int		i, len;
4200Sstevel@tonic-gate 
4210Sstevel@tonic-gate 	len = src->len;
4226557Sfr41279 	while ((len > 1) && (src->value[len - 1] == 0)) {
4230Sstevel@tonic-gate 		len--;
4246557Sfr41279 	}
4250Sstevel@tonic-gate 	src->len = len;
4260Sstevel@tonic-gate 	if (dest->size < len) {
4270Sstevel@tonic-gate 		if (dest->malloced == 1) {
4286557Sfr41279 			newptr = (BIG_CHUNK_TYPE *)big_realloc(dest->value,
4298933Sopensolaris@drydog.com 			    BIGNUM_WORDSIZE * dest->size,
4308933Sopensolaris@drydog.com 			    BIGNUM_WORDSIZE * len);
4310Sstevel@tonic-gate 		} else {
4326557Sfr41279 			newptr = (BIG_CHUNK_TYPE *)
4338933Sopensolaris@drydog.com 			    big_malloc(BIGNUM_WORDSIZE * len);
4346557Sfr41279 			if (newptr != NULL) {
4356557Sfr41279 				dest->malloced = 1;
4366557Sfr41279 			}
4370Sstevel@tonic-gate 		}
4386557Sfr41279 		if (newptr == NULL) {
4390Sstevel@tonic-gate 			return (BIG_NO_MEM);
4406557Sfr41279 		}
4410Sstevel@tonic-gate 		dest->value = newptr;
4420Sstevel@tonic-gate 		dest->size = len;
4430Sstevel@tonic-gate 	}
4440Sstevel@tonic-gate 	dest->len = len;
4450Sstevel@tonic-gate 	dest->sign = src->sign;
4466557Sfr41279 	for (i = 0; i < len; i++) {
4476557Sfr41279 		dest->value[i] = src->value[i];
4486557Sfr41279 	}
4490Sstevel@tonic-gate 
4500Sstevel@tonic-gate 	return (BIG_OK);
4510Sstevel@tonic-gate }
4520Sstevel@tonic-gate 
4530Sstevel@tonic-gate 
4549302Sopensolaris@drydog.com /*
4559302Sopensolaris@drydog.com  * big_extend()
4569302Sopensolaris@drydog.com  * Allocate memory to extend BIGNUM number to size bignum chunks,
4579302Sopensolaris@drydog.com  * if not at least that size already.
4589302Sopensolaris@drydog.com  */
4590Sstevel@tonic-gate BIG_ERR_CODE
big_extend(BIGNUM * number,int size)4600Sstevel@tonic-gate big_extend(BIGNUM *number, int size)
4610Sstevel@tonic-gate {
4626557Sfr41279 	BIG_CHUNK_TYPE	*newptr;
4630Sstevel@tonic-gate 	int		i;
4640Sstevel@tonic-gate 
4650Sstevel@tonic-gate 	if (number->size >= size)
4660Sstevel@tonic-gate 		return (BIG_OK);
4670Sstevel@tonic-gate 	if (number->malloced) {
4686557Sfr41279 		number->value = big_realloc(number->value,
4698933Sopensolaris@drydog.com 		    BIGNUM_WORDSIZE * number->size,
4708933Sopensolaris@drydog.com 		    BIGNUM_WORDSIZE * size);
4710Sstevel@tonic-gate 	} else {
4728933Sopensolaris@drydog.com 		newptr = big_malloc(BIGNUM_WORDSIZE * size);
4730Sstevel@tonic-gate 		if (newptr != NULL) {
4740Sstevel@tonic-gate 			for (i = 0; i < number->size; i++) {
4750Sstevel@tonic-gate 				newptr[i] = number->value[i];
4760Sstevel@tonic-gate 			}
4770Sstevel@tonic-gate 		}
4780Sstevel@tonic-gate 		number->value = newptr;
4790Sstevel@tonic-gate 	}
4800Sstevel@tonic-gate 
4816557Sfr41279 	if (number->value == NULL) {
4820Sstevel@tonic-gate 		return (BIG_NO_MEM);
4836557Sfr41279 	}
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate 	number->size = size;
4860Sstevel@tonic-gate 	number->malloced = 1;
4870Sstevel@tonic-gate 	return (BIG_OK);
4880Sstevel@tonic-gate }
4890Sstevel@tonic-gate 
4900Sstevel@tonic-gate 
4916557Sfr41279 /* returns 1 if n == 0 */
4920Sstevel@tonic-gate int
big_is_zero(BIGNUM * n)4930Sstevel@tonic-gate big_is_zero(BIGNUM *n)
4940Sstevel@tonic-gate {
4956557Sfr41279 	int	i, result;
4960Sstevel@tonic-gate 
4970Sstevel@tonic-gate 	result = 1;
4986557Sfr41279 	for (i = 0; i < n->len; i++) {
4996557Sfr41279 		if (n->value[i] != 0) {
5006557Sfr41279 			result = 0;
5016557Sfr41279 		}
5026557Sfr41279 	}
5030Sstevel@tonic-gate 	return (result);
5040Sstevel@tonic-gate }
5050Sstevel@tonic-gate 
5060Sstevel@tonic-gate 
5070Sstevel@tonic-gate BIG_ERR_CODE
big_add_abs(BIGNUM * result,BIGNUM * aa,BIGNUM * bb)5080Sstevel@tonic-gate big_add_abs(BIGNUM *result, BIGNUM *aa, BIGNUM *bb)
5090Sstevel@tonic-gate {
5106557Sfr41279 	int		i, shorter, longer;
5116557Sfr41279 	BIG_CHUNK_TYPE	cy, ai;
5126557Sfr41279 	BIG_CHUNK_TYPE	*r, *a, *b, *c;
5136557Sfr41279 	BIG_ERR_CODE	err;
5146557Sfr41279 	BIGNUM		*longerarg;
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate 	if (aa->len > bb->len) {
5170Sstevel@tonic-gate 		shorter = bb->len;
5180Sstevel@tonic-gate 		longer = aa->len;
5196557Sfr41279 		longerarg = aa;
5200Sstevel@tonic-gate 	} else {
5210Sstevel@tonic-gate 		shorter = aa->len;
5220Sstevel@tonic-gate 		longer = bb->len;
5236557Sfr41279 		longerarg = bb;
5240Sstevel@tonic-gate 	}
5250Sstevel@tonic-gate 	if (result->size < longer + 1) {
5260Sstevel@tonic-gate 		err = big_extend(result, longer + 1);
5276557Sfr41279 		if (err != BIG_OK) {
5280Sstevel@tonic-gate 			return (err);
5296557Sfr41279 		}
5300Sstevel@tonic-gate 	}
5310Sstevel@tonic-gate 
5320Sstevel@tonic-gate 	r = result->value;
5330Sstevel@tonic-gate 	a = aa->value;
5340Sstevel@tonic-gate 	b = bb->value;
5356557Sfr41279 	c = longerarg->value;
5360Sstevel@tonic-gate 	cy = 0;
5370Sstevel@tonic-gate 	for (i = 0; i < shorter; i++) {
5380Sstevel@tonic-gate 		ai = a[i];
5390Sstevel@tonic-gate 		r[i] = ai + b[i] + cy;
5406557Sfr41279 		if (r[i] > ai) {
5416557Sfr41279 			cy = 0;
5426557Sfr41279 		} else if (r[i] < ai) {
5436557Sfr41279 			cy = 1;
5446557Sfr41279 		}
5450Sstevel@tonic-gate 	}
5460Sstevel@tonic-gate 	for (; i < longer; i++) {
5470Sstevel@tonic-gate 		ai = c[i];
5480Sstevel@tonic-gate 		r[i] = ai + cy;
5496557Sfr41279 		if (r[i] >= ai) {
5506557Sfr41279 			cy = 0;
5516557Sfr41279 		}
5520Sstevel@tonic-gate 	}
5530Sstevel@tonic-gate 	if (cy == 1) {
5540Sstevel@tonic-gate 		r[i] = cy;
5550Sstevel@tonic-gate 		result->len = longer + 1;
5560Sstevel@tonic-gate 	} else {
5570Sstevel@tonic-gate 		result->len = longer;
5580Sstevel@tonic-gate 	}
5590Sstevel@tonic-gate 	result->sign = 1;
5600Sstevel@tonic-gate 	return (BIG_OK);
5610Sstevel@tonic-gate }
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate 
5640Sstevel@tonic-gate /* caller must make sure that result has at least len words allocated */
5650Sstevel@tonic-gate void
big_sub_vec(BIG_CHUNK_TYPE * r,BIG_CHUNK_TYPE * a,BIG_CHUNK_TYPE * b,int len)5666557Sfr41279 big_sub_vec(BIG_CHUNK_TYPE *r, BIG_CHUNK_TYPE *a, BIG_CHUNK_TYPE *b, int len)
5670Sstevel@tonic-gate {
5686557Sfr41279 	int		i;
5696557Sfr41279 	BIG_CHUNK_TYPE	cy, ai;
5700Sstevel@tonic-gate 
5710Sstevel@tonic-gate 	cy = 1;
5720Sstevel@tonic-gate 	for (i = 0; i < len; i++) {
5730Sstevel@tonic-gate 		ai = a[i];
5740Sstevel@tonic-gate 		r[i] = ai + (~b[i]) + cy;
5756557Sfr41279 		if (r[i] > ai) {
5766557Sfr41279 			cy = 0;
5776557Sfr41279 		} else if (r[i] < ai) {
5786557Sfr41279 			cy = 1;
5796557Sfr41279 		}
5800Sstevel@tonic-gate 	}
5810Sstevel@tonic-gate }
5820Sstevel@tonic-gate 
5830Sstevel@tonic-gate 
5840Sstevel@tonic-gate /* result=aa-bb  it is assumed that aa>=bb */
5850Sstevel@tonic-gate BIG_ERR_CODE
big_sub_pos(BIGNUM * result,BIGNUM * aa,BIGNUM * bb)5860Sstevel@tonic-gate big_sub_pos(BIGNUM *result, BIGNUM *aa, BIGNUM *bb)
5870Sstevel@tonic-gate {
5886557Sfr41279 	int		i, shorter;
5896557Sfr41279 	BIG_CHUNK_TYPE	cy = 1, ai;
5906557Sfr41279 	BIG_CHUNK_TYPE	*r, *a, *b;
5916557Sfr41279 	BIG_ERR_CODE	err = BIG_OK;
5926557Sfr41279 
5936557Sfr41279 	if (aa->len > bb->len) {
5946557Sfr41279 		shorter = bb->len;
5956557Sfr41279 	} else {
5966557Sfr41279 		shorter = aa->len;
5976557Sfr41279 	}
5980Sstevel@tonic-gate 	if (result->size < aa->len) {
5990Sstevel@tonic-gate 		err = big_extend(result, aa->len);
6006557Sfr41279 		if (err != BIG_OK) {
6010Sstevel@tonic-gate 			return (err);
6026557Sfr41279 		}
6030Sstevel@tonic-gate 	}
6040Sstevel@tonic-gate 
6050Sstevel@tonic-gate 	r = result->value;
6060Sstevel@tonic-gate 	a = aa->value;
6070Sstevel@tonic-gate 	b = bb->value;
6080Sstevel@tonic-gate 	result->len = aa->len;
6090Sstevel@tonic-gate 	cy = 1;
6100Sstevel@tonic-gate 	for (i = 0; i < shorter; i++) {
6110Sstevel@tonic-gate 		ai = a[i];
6120Sstevel@tonic-gate 		r[i] = ai + (~b[i]) + cy;
6136557Sfr41279 		if (r[i] > ai) {
6146557Sfr41279 			cy = 0;
6156557Sfr41279 		} else if (r[i] < ai) {
6166557Sfr41279 			cy = 1;
6176557Sfr41279 		}
6180Sstevel@tonic-gate 	}
6190Sstevel@tonic-gate 	for (; i < aa->len; i++) {
6200Sstevel@tonic-gate 		ai = a[i];
6210Sstevel@tonic-gate 		r[i] = ai + (~0) + cy;
6226557Sfr41279 		if (r[i] < ai) {
6236557Sfr41279 			cy = 1;
6246557Sfr41279 		}
6250Sstevel@tonic-gate 	}
6260Sstevel@tonic-gate 	result->sign = 1;
6276557Sfr41279 
6286557Sfr41279 	if (cy == 0) {
6290Sstevel@tonic-gate 		return (BIG_INVALID_ARGS);
6306557Sfr41279 	} else {
6310Sstevel@tonic-gate 		return (BIG_OK);
6326557Sfr41279 	}
6330Sstevel@tonic-gate }
6340Sstevel@tonic-gate 
6350Sstevel@tonic-gate 
6360Sstevel@tonic-gate /* returns -1 if |aa|<|bb|, 0 if |aa|==|bb| 1 if |aa|>|bb| */
6370Sstevel@tonic-gate int
big_cmp_abs(BIGNUM * aa,BIGNUM * bb)6380Sstevel@tonic-gate big_cmp_abs(BIGNUM *aa, BIGNUM *bb)
6390Sstevel@tonic-gate {
6406557Sfr41279 	int	i;
6410Sstevel@tonic-gate 
6420Sstevel@tonic-gate 	if (aa->len > bb->len) {
6430Sstevel@tonic-gate 		for (i = aa->len - 1; i > bb->len - 1; i--) {
6446557Sfr41279 			if (aa->value[i] > 0) {
6450Sstevel@tonic-gate 				return (1);
6466557Sfr41279 			}
6470Sstevel@tonic-gate 		}
6480Sstevel@tonic-gate 	} else if (aa->len < bb->len) {
6490Sstevel@tonic-gate 		for (i = bb->len - 1; i > aa->len - 1; i--) {
6506557Sfr41279 			if (bb->value[i] > 0) {
6510Sstevel@tonic-gate 				return (-1);
6526557Sfr41279 			}
6530Sstevel@tonic-gate 		}
6546557Sfr41279 	} else {
6558933Sopensolaris@drydog.com 		i = aa->len - 1;
6566557Sfr41279 	}
6570Sstevel@tonic-gate 	for (; i >= 0; i--) {
6586557Sfr41279 		if (aa->value[i] > bb->value[i]) {
6590Sstevel@tonic-gate 			return (1);
6606557Sfr41279 		} else if (aa->value[i] < bb->value[i]) {
6610Sstevel@tonic-gate 			return (-1);
6626557Sfr41279 		}
6630Sstevel@tonic-gate 	}
6640Sstevel@tonic-gate 
6650Sstevel@tonic-gate 	return (0);
6660Sstevel@tonic-gate }
6670Sstevel@tonic-gate 
6680Sstevel@tonic-gate 
6690Sstevel@tonic-gate BIG_ERR_CODE
big_sub(BIGNUM * result,BIGNUM * aa,BIGNUM * bb)6700Sstevel@tonic-gate big_sub(BIGNUM *result, BIGNUM *aa, BIGNUM *bb)
6710Sstevel@tonic-gate {
6726557Sfr41279 	BIG_ERR_CODE	err;
6730Sstevel@tonic-gate 
6740Sstevel@tonic-gate 	if ((bb->sign == -1) && (aa->sign == 1)) {
6756557Sfr41279 		if ((err = big_add_abs(result, aa, bb)) != BIG_OK) {
6760Sstevel@tonic-gate 			return (err);
6776557Sfr41279 		}
6780Sstevel@tonic-gate 		result->sign = 1;
6790Sstevel@tonic-gate 	} else if ((aa->sign == -1) && (bb->sign == 1)) {
6806557Sfr41279 		if ((err = big_add_abs(result, aa, bb)) != BIG_OK) {
6810Sstevel@tonic-gate 			return (err);
6826557Sfr41279 		}
6830Sstevel@tonic-gate 		result->sign = -1;
6840Sstevel@tonic-gate 	} else if ((aa->sign == 1) && (bb->sign == 1)) {
6850Sstevel@tonic-gate 		if (big_cmp_abs(aa, bb) >= 0) {
6866557Sfr41279 			if ((err = big_sub_pos(result, aa, bb)) != BIG_OK) {
6870Sstevel@tonic-gate 				return (err);
6886557Sfr41279 			}
6890Sstevel@tonic-gate 			result->sign = 1;
6900Sstevel@tonic-gate 		} else {
6916557Sfr41279 			if ((err = big_sub_pos(result, bb, aa)) != BIG_OK) {
6920Sstevel@tonic-gate 				return (err);
6936557Sfr41279 			}
6940Sstevel@tonic-gate 			result->sign = -1;
6950Sstevel@tonic-gate 		}
6960Sstevel@tonic-gate 	} else {
6970Sstevel@tonic-gate 		if (big_cmp_abs(aa, bb) >= 0) {
6986557Sfr41279 			if ((err = big_sub_pos(result, aa, bb)) != BIG_OK) {
6990Sstevel@tonic-gate 				return (err);
7006557Sfr41279 			}
7010Sstevel@tonic-gate 			result->sign = -1;
7020Sstevel@tonic-gate 		} else {
7036557Sfr41279 			if ((err = big_sub_pos(result, bb, aa)) != BIG_OK) {
7040Sstevel@tonic-gate 				return (err);
7056557Sfr41279 			}
7060Sstevel@tonic-gate 			result->sign = 1;
7070Sstevel@tonic-gate 		}
7080Sstevel@tonic-gate 	}
7090Sstevel@tonic-gate 	return (BIG_OK);
7100Sstevel@tonic-gate }
7110Sstevel@tonic-gate 
7120Sstevel@tonic-gate 
7130Sstevel@tonic-gate BIG_ERR_CODE
big_add(BIGNUM * result,BIGNUM * aa,BIGNUM * bb)7140Sstevel@tonic-gate big_add(BIGNUM *result, BIGNUM *aa, BIGNUM *bb)
7150Sstevel@tonic-gate {
7166557Sfr41279 	BIG_ERR_CODE	err;
7170Sstevel@tonic-gate 
7180Sstevel@tonic-gate 	if ((bb->sign == -1) && (aa->sign == -1)) {
7196557Sfr41279 		if ((err = big_add_abs(result, aa, bb)) != BIG_OK) {
7200Sstevel@tonic-gate 			return (err);
7216557Sfr41279 		}
7220Sstevel@tonic-gate 		result->sign = -1;
7230Sstevel@tonic-gate 	} else if ((aa->sign == 1) && (bb->sign == 1)) {
7246557Sfr41279 		if ((err = big_add_abs(result, aa, bb)) != BIG_OK) {
7250Sstevel@tonic-gate 			return (err);
7266557Sfr41279 		}
7270Sstevel@tonic-gate 		result->sign = 1;
7280Sstevel@tonic-gate 	} else if ((aa->sign == 1) && (bb->sign == -1)) {
7290Sstevel@tonic-gate 		if (big_cmp_abs(aa, bb) >= 0) {
7306557Sfr41279 			if ((err = big_sub_pos(result, aa, bb)) != BIG_OK) {
7310Sstevel@tonic-gate 				return (err);
7326557Sfr41279 			}
7330Sstevel@tonic-gate 			result->sign = 1;
7340Sstevel@tonic-gate 		} else {
7356557Sfr41279 			if ((err = big_sub_pos(result, bb, aa)) != BIG_OK) {
7360Sstevel@tonic-gate 				return (err);
7376557Sfr41279 			}
7380Sstevel@tonic-gate 			result->sign = -1;
7390Sstevel@tonic-gate 		}
7400Sstevel@tonic-gate 	} else {
7410Sstevel@tonic-gate 		if (big_cmp_abs(aa, bb) >= 0) {
7426557Sfr41279 			if ((err = big_sub_pos(result, aa, bb)) != BIG_OK) {
7430Sstevel@tonic-gate 				return (err);
7446557Sfr41279 			}
7450Sstevel@tonic-gate 			result->sign = -1;
7460Sstevel@tonic-gate 		} else {
7476557Sfr41279 			if ((err = big_sub_pos(result, bb, aa)) != BIG_OK) {
7480Sstevel@tonic-gate 				return (err);
7496557Sfr41279 			}
7500Sstevel@tonic-gate 			result->sign = 1;
7510Sstevel@tonic-gate 		}
7520Sstevel@tonic-gate 	}
7530Sstevel@tonic-gate 	return (BIG_OK);
7540Sstevel@tonic-gate }
7550Sstevel@tonic-gate 
7560Sstevel@tonic-gate 
7576557Sfr41279 /* result = aa/2 */
7580Sstevel@tonic-gate BIG_ERR_CODE
big_half_pos(BIGNUM * result,BIGNUM * aa)7590Sstevel@tonic-gate big_half_pos(BIGNUM *result, BIGNUM *aa)
7600Sstevel@tonic-gate {
7616557Sfr41279 	BIG_ERR_CODE	err;
7626557Sfr41279 	int		i;
7636557Sfr41279 	BIG_CHUNK_TYPE	cy, cy1;
7646557Sfr41279 	BIG_CHUNK_TYPE	*a, *r;
7650Sstevel@tonic-gate 
7660Sstevel@tonic-gate 	if (result->size < aa->len) {
7670Sstevel@tonic-gate 		err = big_extend(result, aa->len);
7686557Sfr41279 		if (err != BIG_OK) {
7690Sstevel@tonic-gate 			return (err);
7706557Sfr41279 		}
7710Sstevel@tonic-gate 	}
7720Sstevel@tonic-gate 
7730Sstevel@tonic-gate 	result->len = aa->len;
7740Sstevel@tonic-gate 	a = aa->value;
7750Sstevel@tonic-gate 	r = result->value;
7760Sstevel@tonic-gate 	cy = 0;
7776557Sfr41279 	for (i = aa->len - 1; i >= 0; i--) {
7786557Sfr41279 		cy1 = a[i] << (BIG_CHUNK_SIZE - 1);
7796557Sfr41279 		r[i] = (cy | (a[i] >> 1));
7800Sstevel@tonic-gate 		cy = cy1;
7810Sstevel@tonic-gate 	}
7826557Sfr41279 	if (r[result->len - 1] == 0) {
7836557Sfr41279 		result->len--;
7846557Sfr41279 	}
7856557Sfr41279 
7860Sstevel@tonic-gate 	return (BIG_OK);
7870Sstevel@tonic-gate }
7880Sstevel@tonic-gate 
7896557Sfr41279 /* result  =  aa*2 */
7900Sstevel@tonic-gate BIG_ERR_CODE
big_double(BIGNUM * result,BIGNUM * aa)7910Sstevel@tonic-gate big_double(BIGNUM *result, BIGNUM *aa)
7920Sstevel@tonic-gate {
7936557Sfr41279 	BIG_ERR_CODE	err;
7946557Sfr41279 	int		i, rsize;
7956557Sfr41279 	BIG_CHUNK_TYPE	cy, cy1;
7966557Sfr41279 	BIG_CHUNK_TYPE	*a, *r;
7976557Sfr41279 
7986557Sfr41279 	if ((aa->len > 0) &&
7996557Sfr41279 	    ((aa->value[aa->len - 1] & BIG_CHUNK_HIGHBIT) != 0)) {
8000Sstevel@tonic-gate 		rsize = aa->len + 1;
8016557Sfr41279 	} else {
8026557Sfr41279 		rsize = aa->len;
8036557Sfr41279 	}
8040Sstevel@tonic-gate 
8050Sstevel@tonic-gate 	if (result->size < rsize) {
8060Sstevel@tonic-gate 		err = big_extend(result, rsize);
8070Sstevel@tonic-gate 		if (err != BIG_OK)
8080Sstevel@tonic-gate 			return (err);
8090Sstevel@tonic-gate 	}
8100Sstevel@tonic-gate 
8110Sstevel@tonic-gate 	a = aa->value;
8120Sstevel@tonic-gate 	r = result->value;
8136557Sfr41279 	if (rsize == aa->len + 1) {
8146557Sfr41279 		r[rsize - 1] = 1;
8156557Sfr41279 	}
8160Sstevel@tonic-gate 	cy = 0;
8170Sstevel@tonic-gate 	for (i = 0; i < aa->len; i++) {
8186557Sfr41279 		cy1 = a[i] >> (BIG_CHUNK_SIZE - 1);
8190Sstevel@tonic-gate 		r[i] = (cy | (a[i] << 1));
8200Sstevel@tonic-gate 		cy = cy1;
8210Sstevel@tonic-gate 	}
8220Sstevel@tonic-gate 	result->len = rsize;
8230Sstevel@tonic-gate 	return (BIG_OK);
8240Sstevel@tonic-gate }
8250Sstevel@tonic-gate 
8266557Sfr41279 
8276557Sfr41279 /*
8286557Sfr41279  * returns aa mod b, aa must be nonneg, b must be a max
8296557Sfr41279  * (BIG_CHUNK_SIZE / 2)-bit integer
8306557Sfr41279  */
8316557Sfr41279 static uint32_t
big_modhalf_pos(BIGNUM * aa,uint32_t b)8326557Sfr41279 big_modhalf_pos(BIGNUM *aa, uint32_t b)
8330Sstevel@tonic-gate {
8346557Sfr41279 	int		i;
8356557Sfr41279 	BIG_CHUNK_TYPE	rem;
8366557Sfr41279 
8376557Sfr41279 	if (aa->len == 0) {
8380Sstevel@tonic-gate 		return (0);
8396557Sfr41279 	}
8400Sstevel@tonic-gate 	rem = aa->value[aa->len - 1] % b;
8410Sstevel@tonic-gate 	for (i = aa->len - 2; i >= 0; i--) {
8426557Sfr41279 		rem = ((rem << (BIG_CHUNK_SIZE / 2)) |
8436557Sfr41279 		    (aa->value[i] >> (BIG_CHUNK_SIZE / 2))) % b;
8446557Sfr41279 		rem = ((rem << (BIG_CHUNK_SIZE / 2)) |
8456557Sfr41279 		    (aa->value[i] & BIG_CHUNK_LOWHALFBITS)) % b;
8460Sstevel@tonic-gate 	}
8476557Sfr41279 
8486557Sfr41279 	return ((uint32_t)rem);
8490Sstevel@tonic-gate }
8500Sstevel@tonic-gate 
8510Sstevel@tonic-gate 
8520Sstevel@tonic-gate /*
8536557Sfr41279  * result = aa - (2^BIG_CHUNK_SIZE)^lendiff * bb
8540Sstevel@tonic-gate  * result->size should be at least aa->len at entry
8550Sstevel@tonic-gate  * aa, bb, and result should be positive
8560Sstevel@tonic-gate  */
8570Sstevel@tonic-gate void
big_sub_pos_high(BIGNUM * result,BIGNUM * aa,BIGNUM * bb)8580Sstevel@tonic-gate big_sub_pos_high(BIGNUM *result, BIGNUM *aa, BIGNUM *bb)
8590Sstevel@tonic-gate {
8600Sstevel@tonic-gate 	int i, lendiff;
8610Sstevel@tonic-gate 	BIGNUM res1, aa1;
8620Sstevel@tonic-gate 
8630Sstevel@tonic-gate 	lendiff = aa->len - bb->len;
8640Sstevel@tonic-gate 	res1.size = result->size - lendiff;
8650Sstevel@tonic-gate 	res1.malloced = 0;
8660Sstevel@tonic-gate 	res1.value = result->value + lendiff;
8670Sstevel@tonic-gate 	aa1.size = aa->size - lendiff;
8680Sstevel@tonic-gate 	aa1.value = aa->value + lendiff;
8690Sstevel@tonic-gate 	aa1.len = bb->len;
8700Sstevel@tonic-gate 	aa1.sign = 1;
8710Sstevel@tonic-gate 	(void) big_sub_pos(&res1, &aa1, bb);
8720Sstevel@tonic-gate 	if (result->value != aa->value) {
8730Sstevel@tonic-gate 		for (i = 0; i < lendiff; i++) {
8740Sstevel@tonic-gate 			result->value[i] = aa->value[i];
8750Sstevel@tonic-gate 		}
8760Sstevel@tonic-gate 	}
8770Sstevel@tonic-gate 	result->len = aa->len;
8780Sstevel@tonic-gate }
8790Sstevel@tonic-gate 
8800Sstevel@tonic-gate 
8810Sstevel@tonic-gate /*
8820Sstevel@tonic-gate  * returns 1, 0, or -1 depending on whether |aa| > , ==, or <
8836557Sfr41279  *					(2^BIG_CHUNK_SIZE)^lendiff * |bb|
8840Sstevel@tonic-gate  * aa->len should be >= bb->len
8850Sstevel@tonic-gate  */
8860Sstevel@tonic-gate int
big_cmp_abs_high(BIGNUM * aa,BIGNUM * bb)8870Sstevel@tonic-gate big_cmp_abs_high(BIGNUM *aa, BIGNUM *bb)
8880Sstevel@tonic-gate {
8898933Sopensolaris@drydog.com 	int		lendiff;
8908933Sopensolaris@drydog.com 	BIGNUM		aa1;
8910Sstevel@tonic-gate 
8920Sstevel@tonic-gate 	lendiff = aa->len - bb->len;
8930Sstevel@tonic-gate 	aa1.len = bb->len;
8940Sstevel@tonic-gate 	aa1.size = aa->size - lendiff;
8950Sstevel@tonic-gate 	aa1.malloced = 0;
8960Sstevel@tonic-gate 	aa1.value = aa->value + lendiff;
8970Sstevel@tonic-gate 	return (big_cmp_abs(&aa1, bb));
8980Sstevel@tonic-gate }
8990Sstevel@tonic-gate 
9000Sstevel@tonic-gate 
9010Sstevel@tonic-gate /*
9026557Sfr41279  * result = aa * b where b is a max. (BIG_CHUNK_SIZE / 2)-bit positive integer.
9030Sstevel@tonic-gate  * result should have enough space allocated.
9040Sstevel@tonic-gate  */
9056557Sfr41279 static void
big_mulhalf_low(BIGNUM * result,BIGNUM * aa,BIG_CHUNK_TYPE b)9066557Sfr41279 big_mulhalf_low(BIGNUM *result, BIGNUM *aa, BIG_CHUNK_TYPE b)
9070Sstevel@tonic-gate {
9086557Sfr41279 	int		i;
9096557Sfr41279 	BIG_CHUNK_TYPE	t1, t2, ai, cy;
9106557Sfr41279 	BIG_CHUNK_TYPE	*a, *r;
9110Sstevel@tonic-gate 
9120Sstevel@tonic-gate 	a = aa->value;
9130Sstevel@tonic-gate 	r = result->value;
9140Sstevel@tonic-gate 	cy = 0;
9150Sstevel@tonic-gate 	for (i = 0; i < aa->len; i++) {
9160Sstevel@tonic-gate 		ai = a[i];
9176557Sfr41279 		t1 = (ai & BIG_CHUNK_LOWHALFBITS) * b + cy;
9186557Sfr41279 		t2 = (ai >> (BIG_CHUNK_SIZE / 2)) * b +
9196557Sfr41279 		    (t1 >> (BIG_CHUNK_SIZE / 2));
9206557Sfr41279 		r[i] = (t1 & BIG_CHUNK_LOWHALFBITS) |
9216557Sfr41279 		    (t2 << (BIG_CHUNK_SIZE / 2));
9226557Sfr41279 		cy = t2 >> (BIG_CHUNK_SIZE / 2);
9230Sstevel@tonic-gate 	}
9240Sstevel@tonic-gate 	r[i] = cy;
9250Sstevel@tonic-gate 	result->len = aa->len + 1;
9260Sstevel@tonic-gate 	result->sign = aa->sign;
9270Sstevel@tonic-gate }
9280Sstevel@tonic-gate 
9290Sstevel@tonic-gate 
9300Sstevel@tonic-gate /*
9316557Sfr41279  * result = aa * b * 2^(BIG_CHUNK_SIZE / 2) where b is a max.
9326557Sfr41279  * (BIG_CHUNK_SIZE / 2)-bit positive integer.
9330Sstevel@tonic-gate  * result should have enough space allocated.
9340Sstevel@tonic-gate  */
9356557Sfr41279 static void
big_mulhalf_high(BIGNUM * result,BIGNUM * aa,BIG_CHUNK_TYPE b)9366557Sfr41279 big_mulhalf_high(BIGNUM *result, BIGNUM *aa, BIG_CHUNK_TYPE b)
9370Sstevel@tonic-gate {
9386557Sfr41279 	int		i;
9396557Sfr41279 	BIG_CHUNK_TYPE	t1, t2, ai, cy, ri;
9406557Sfr41279 	BIG_CHUNK_TYPE	*a, *r;
9410Sstevel@tonic-gate 
9420Sstevel@tonic-gate 	a = aa->value;
9430Sstevel@tonic-gate 	r = result->value;
9440Sstevel@tonic-gate 	cy = 0;
9450Sstevel@tonic-gate 	ri = 0;
9460Sstevel@tonic-gate 	for (i = 0; i < aa->len; i++) {
9470Sstevel@tonic-gate 		ai = a[i];
9486557Sfr41279 		t1 = (ai & BIG_CHUNK_LOWHALFBITS) * b + cy;
9496557Sfr41279 		t2 = (ai >>  (BIG_CHUNK_SIZE / 2)) * b +
9506557Sfr41279 		    (t1 >>  (BIG_CHUNK_SIZE / 2));
9516557Sfr41279 		r[i] = (t1 <<  (BIG_CHUNK_SIZE / 2)) + ri;
9526557Sfr41279 		ri = t2 & BIG_CHUNK_LOWHALFBITS;
9536557Sfr41279 		cy = t2 >> (BIG_CHUNK_SIZE / 2);
9540Sstevel@tonic-gate 	}
9556557Sfr41279 	r[i] = (cy <<  (BIG_CHUNK_SIZE / 2)) + ri;
9560Sstevel@tonic-gate 	result->len = aa->len + 1;
9570Sstevel@tonic-gate 	result->sign = aa->sign;
9580Sstevel@tonic-gate }
9590Sstevel@tonic-gate 
9606557Sfr41279 
9610Sstevel@tonic-gate /* it is assumed that result->size is big enough */
9620Sstevel@tonic-gate void
big_shiftleft(BIGNUM * result,BIGNUM * aa,int offs)9630Sstevel@tonic-gate big_shiftleft(BIGNUM *result, BIGNUM *aa, int offs)
9640Sstevel@tonic-gate {
9656557Sfr41279 	int		i;
9666557Sfr41279 	BIG_CHUNK_TYPE	cy, ai;
9670Sstevel@tonic-gate 
9680Sstevel@tonic-gate 	if (offs == 0) {
9690Sstevel@tonic-gate 		if (result != aa) {
9700Sstevel@tonic-gate 			(void) big_copy(result, aa);
9710Sstevel@tonic-gate 		}
9720Sstevel@tonic-gate 		return;
9730Sstevel@tonic-gate 	}
9740Sstevel@tonic-gate 	cy = 0;
9750Sstevel@tonic-gate 	for (i = 0; i < aa->len; i++) {
9760Sstevel@tonic-gate 		ai = aa->value[i];
9770Sstevel@tonic-gate 		result->value[i] = (ai << offs) | cy;
9786557Sfr41279 		cy = ai >> (BIG_CHUNK_SIZE - offs);
9790Sstevel@tonic-gate 	}
9800Sstevel@tonic-gate 	if (cy != 0) {
9810Sstevel@tonic-gate 		result->len = aa->len + 1;
9820Sstevel@tonic-gate 		result->value[result->len - 1] = cy;
9830Sstevel@tonic-gate 	} else {
9840Sstevel@tonic-gate 		result->len = aa->len;
9850Sstevel@tonic-gate 	}
9860Sstevel@tonic-gate 	result->sign = aa->sign;
9870Sstevel@tonic-gate }
9880Sstevel@tonic-gate 
9896557Sfr41279 
9900Sstevel@tonic-gate /* it is assumed that result->size is big enough */
9910Sstevel@tonic-gate void
big_shiftright(BIGNUM * result,BIGNUM * aa,int offs)9920Sstevel@tonic-gate big_shiftright(BIGNUM *result, BIGNUM *aa, int offs)
9930Sstevel@tonic-gate {
9946557Sfr41279 	int		 i;
9956557Sfr41279 	BIG_CHUNK_TYPE	cy, ai;
9960Sstevel@tonic-gate 
9970Sstevel@tonic-gate 	if (offs == 0) {
9980Sstevel@tonic-gate 		if (result != aa) {
9990Sstevel@tonic-gate 			(void) big_copy(result, aa);
10000Sstevel@tonic-gate 		}
10010Sstevel@tonic-gate 		return;
10020Sstevel@tonic-gate 	}
10030Sstevel@tonic-gate 	cy = aa->value[0] >> offs;
10040Sstevel@tonic-gate 	for (i = 1; i < aa->len; i++) {
10050Sstevel@tonic-gate 		ai = aa->value[i];
10068933Sopensolaris@drydog.com 		result->value[i - 1] = (ai << (BIG_CHUNK_SIZE - offs)) | cy;
10070Sstevel@tonic-gate 		cy = ai >> offs;
10080Sstevel@tonic-gate 	}
10090Sstevel@tonic-gate 	result->len = aa->len;
10100Sstevel@tonic-gate 	result->value[result->len - 1] = cy;
10110Sstevel@tonic-gate 	result->sign = aa->sign;
10120Sstevel@tonic-gate }
10130Sstevel@tonic-gate 
10140Sstevel@tonic-gate 
10150Sstevel@tonic-gate /*
10160Sstevel@tonic-gate  * result = aa/bb   remainder = aa mod bb
10170Sstevel@tonic-gate  * it is assumed that aa and bb are positive
10180Sstevel@tonic-gate  */
10190Sstevel@tonic-gate BIG_ERR_CODE
big_div_pos(BIGNUM * result,BIGNUM * remainder,BIGNUM * aa,BIGNUM * bb)10208933Sopensolaris@drydog.com big_div_pos(BIGNUM *result, BIGNUM *remainder, BIGNUM *aa, BIGNUM *bb)
10210Sstevel@tonic-gate {
10226557Sfr41279 	BIG_ERR_CODE	err = BIG_OK;
10236557Sfr41279 	int		i, alen, blen, tlen, rlen, offs;
10246557Sfr41279 	BIG_CHUNK_TYPE	higha, highb, coeff;
10256557Sfr41279 	BIG_CHUNK_TYPE	*a, *b;
10266557Sfr41279 	BIGNUM		bbhigh, bblow, tresult, tmp1, tmp2;
10276557Sfr41279 	BIG_CHUNK_TYPE	tmp1value[BIGTMPSIZE];
10286557Sfr41279 	BIG_CHUNK_TYPE	tmp2value[BIGTMPSIZE];
10296557Sfr41279 	BIG_CHUNK_TYPE	tresultvalue[BIGTMPSIZE];
10306557Sfr41279 	BIG_CHUNK_TYPE	bblowvalue[BIGTMPSIZE];
10316557Sfr41279 	BIG_CHUNK_TYPE	bbhighvalue[BIGTMPSIZE];
10320Sstevel@tonic-gate 
10330Sstevel@tonic-gate 	a = aa->value;
10340Sstevel@tonic-gate 	b = bb->value;
10350Sstevel@tonic-gate 	alen = aa->len;
10360Sstevel@tonic-gate 	blen = bb->len;
10376557Sfr41279 	while ((alen > 1) && (a[alen - 1] == 0)) {
10386557Sfr41279 		alen = alen - 1;
10396557Sfr41279 	}
10400Sstevel@tonic-gate 	aa->len = alen;
10416557Sfr41279 	while ((blen > 1) && (b[blen - 1] == 0)) {
10426557Sfr41279 		blen = blen - 1;
10436557Sfr41279 	}
10440Sstevel@tonic-gate 	bb->len = blen;
10456557Sfr41279 	if ((blen == 1) && (b[0] == 0)) {
10460Sstevel@tonic-gate 		return (BIG_DIV_BY_0);
10476557Sfr41279 	}
10480Sstevel@tonic-gate 
10490Sstevel@tonic-gate 	if (big_cmp_abs(aa, bb) < 0) {
10500Sstevel@tonic-gate 		if ((remainder != NULL) &&
10516557Sfr41279 		    ((err = big_copy(remainder, aa)) != BIG_OK)) {
10520Sstevel@tonic-gate 			return (err);
10536557Sfr41279 		}
10540Sstevel@tonic-gate 		if (result != NULL) {
10550Sstevel@tonic-gate 			result->len = 1;
10560Sstevel@tonic-gate 			result->sign = 1;
10570Sstevel@tonic-gate 			result->value[0] = 0;
10580Sstevel@tonic-gate 		}
10590Sstevel@tonic-gate 		return (BIG_OK);
10600Sstevel@tonic-gate 	}
10610Sstevel@tonic-gate 
10620Sstevel@tonic-gate 	if ((err = big_init1(&bblow, blen + 1,
10630Sstevel@tonic-gate 	    bblowvalue, arraysize(bblowvalue))) != BIG_OK)
10640Sstevel@tonic-gate 		return (err);
10650Sstevel@tonic-gate 
10660Sstevel@tonic-gate 	if ((err = big_init1(&bbhigh, blen + 1,
10670Sstevel@tonic-gate 	    bbhighvalue, arraysize(bbhighvalue))) != BIG_OK)
10680Sstevel@tonic-gate 		goto ret1;
10690Sstevel@tonic-gate 
10700Sstevel@tonic-gate 	if ((err = big_init1(&tmp1, alen + 2,
10710Sstevel@tonic-gate 	    tmp1value, arraysize(tmp1value))) != BIG_OK)
10720Sstevel@tonic-gate 		goto ret2;
10730Sstevel@tonic-gate 
10740Sstevel@tonic-gate 	if ((err = big_init1(&tmp2, blen + 2,
10750Sstevel@tonic-gate 	    tmp2value, arraysize(tmp2value))) != BIG_OK)
10760Sstevel@tonic-gate 		goto ret3;
10770Sstevel@tonic-gate 
10780Sstevel@tonic-gate 	if ((err = big_init1(&tresult, alen - blen + 2,
10790Sstevel@tonic-gate 	    tresultvalue, arraysize(tresultvalue))) != BIG_OK)
10800Sstevel@tonic-gate 		goto ret4;
10810Sstevel@tonic-gate 
10820Sstevel@tonic-gate 	offs = 0;
10836557Sfr41279 	highb = b[blen - 1];
10846557Sfr41279 	if (highb >= (BIG_CHUNK_HALF_HIGHBIT << 1)) {
10856557Sfr41279 		highb = highb >> (BIG_CHUNK_SIZE / 2);
10866557Sfr41279 		offs = (BIG_CHUNK_SIZE / 2);
10870Sstevel@tonic-gate 	}
10886557Sfr41279 	while ((highb & BIG_CHUNK_HALF_HIGHBIT) == 0) {
10896557Sfr41279 		highb = highb << 1;
10900Sstevel@tonic-gate 		offs++;
10910Sstevel@tonic-gate 	}
10920Sstevel@tonic-gate 
10930Sstevel@tonic-gate 	big_shiftleft(&bblow, bb, offs);
10946557Sfr41279 
10956557Sfr41279 	if (offs <= (BIG_CHUNK_SIZE / 2 - 1)) {
10966557Sfr41279 		big_shiftleft(&bbhigh, &bblow, BIG_CHUNK_SIZE / 2);
10970Sstevel@tonic-gate 	} else {
10986557Sfr41279 		big_shiftright(&bbhigh, &bblow, BIG_CHUNK_SIZE / 2);
10990Sstevel@tonic-gate 	}
11000Sstevel@tonic-gate 	if (bbhigh.value[bbhigh.len - 1] == 0) {
11010Sstevel@tonic-gate 		bbhigh.len--;
11020Sstevel@tonic-gate 	} else {
11030Sstevel@tonic-gate 		bbhigh.value[bbhigh.len] = 0;
11040Sstevel@tonic-gate 	}
11050Sstevel@tonic-gate 
11066557Sfr41279 	highb = bblow.value[bblow.len - 1];
11076557Sfr41279 
11080Sstevel@tonic-gate 	big_shiftleft(&tmp1, aa, offs);
11090Sstevel@tonic-gate 	rlen = tmp1.len - bblow.len + 1;
11100Sstevel@tonic-gate 	tresult.len = rlen;
11110Sstevel@tonic-gate 
11120Sstevel@tonic-gate 	tmp1.len++;
11130Sstevel@tonic-gate 	tlen = tmp1.len;
11140Sstevel@tonic-gate 	tmp1.value[tmp1.len - 1] = 0;
11150Sstevel@tonic-gate 	for (i = 0; i < rlen; i++) {
11166557Sfr41279 		higha = (tmp1.value[tlen - 1] << (BIG_CHUNK_SIZE / 2)) +
11176557Sfr41279 		    (tmp1.value[tlen - 2] >> (BIG_CHUNK_SIZE / 2));
11180Sstevel@tonic-gate 		coeff = higha / (highb + 1);
11196557Sfr41279 		big_mulhalf_high(&tmp2, &bblow, coeff);
11200Sstevel@tonic-gate 		big_sub_pos_high(&tmp1, &tmp1, &tmp2);
11210Sstevel@tonic-gate 		bbhigh.len++;
11220Sstevel@tonic-gate 		while (tmp1.value[tlen - 1] > 0) {
11230Sstevel@tonic-gate 			big_sub_pos_high(&tmp1, &tmp1, &bbhigh);
11240Sstevel@tonic-gate 			coeff++;
11250Sstevel@tonic-gate 		}
11260Sstevel@tonic-gate 		bbhigh.len--;
11270Sstevel@tonic-gate 		tlen--;
11280Sstevel@tonic-gate 		tmp1.len--;
11290Sstevel@tonic-gate 		while (big_cmp_abs_high(&tmp1, &bbhigh) >= 0) {
11300Sstevel@tonic-gate 			big_sub_pos_high(&tmp1, &tmp1, &bbhigh);
11310Sstevel@tonic-gate 			coeff++;
11320Sstevel@tonic-gate 		}
11336557Sfr41279 		tresult.value[rlen - i - 1] = coeff << (BIG_CHUNK_SIZE / 2);
11340Sstevel@tonic-gate 		higha = tmp1.value[tlen - 1];
11350Sstevel@tonic-gate 		coeff = higha / (highb + 1);
11366557Sfr41279 		big_mulhalf_low(&tmp2, &bblow, coeff);
11370Sstevel@tonic-gate 		tmp2.len--;
11380Sstevel@tonic-gate 		big_sub_pos_high(&tmp1, &tmp1, &tmp2);
11390Sstevel@tonic-gate 		while (big_cmp_abs_high(&tmp1, &bblow) >= 0) {
11400Sstevel@tonic-gate 			big_sub_pos_high(&tmp1, &tmp1, &bblow);
11410Sstevel@tonic-gate 			coeff++;
11420Sstevel@tonic-gate 		}
11430Sstevel@tonic-gate 		tresult.value[rlen - i - 1] =
11440Sstevel@tonic-gate 		    tresult.value[rlen - i - 1] + coeff;
11450Sstevel@tonic-gate 	}
11460Sstevel@tonic-gate 
11470Sstevel@tonic-gate 	big_shiftright(&tmp1, &tmp1, offs);
11480Sstevel@tonic-gate 
11490Sstevel@tonic-gate 	err = BIG_OK;
11500Sstevel@tonic-gate 
11510Sstevel@tonic-gate 	if ((remainder != NULL) &&
11520Sstevel@tonic-gate 	    ((err = big_copy(remainder, &tmp1)) != BIG_OK))
11530Sstevel@tonic-gate 		goto ret;
11540Sstevel@tonic-gate 
11550Sstevel@tonic-gate 	if (result != NULL)
11560Sstevel@tonic-gate 		err = big_copy(result, &tresult);
11570Sstevel@tonic-gate 
11580Sstevel@tonic-gate ret:
11590Sstevel@tonic-gate 	big_finish(&tresult);
11600Sstevel@tonic-gate ret4:
11610Sstevel@tonic-gate 	big_finish(&tmp1);
11620Sstevel@tonic-gate ret3:
11630Sstevel@tonic-gate 	big_finish(&tmp2);
11640Sstevel@tonic-gate ret2:
11650Sstevel@tonic-gate 	big_finish(&bbhigh);
11660Sstevel@tonic-gate ret1:
11670Sstevel@tonic-gate 	big_finish(&bblow);
11680Sstevel@tonic-gate 	return (err);
11690Sstevel@tonic-gate }
11700Sstevel@tonic-gate 
11718933Sopensolaris@drydog.com 
11720Sstevel@tonic-gate /*
11730Sstevel@tonic-gate  * If there is no processor-specific integer implementation of
11740Sstevel@tonic-gate  * the lower level multiply functions, then this code is provided
11750Sstevel@tonic-gate  * for big_mul_set_vec(), big_mul_add_vec(), big_mul_vec() and
11760Sstevel@tonic-gate  * big_sqr_vec().
11770Sstevel@tonic-gate  *
11780Sstevel@tonic-gate  * There are two generic implementations.  One that assumes that
11790Sstevel@tonic-gate  * there is hardware and C compiler support for a 32 x 32 --> 64
11800Sstevel@tonic-gate  * bit unsigned multiply, but otherwise is not specific to any
11810Sstevel@tonic-gate  * processor, platform, or ISA.
11820Sstevel@tonic-gate  *
11830Sstevel@tonic-gate  * The other makes very few assumptions about hardware capabilities.
11840Sstevel@tonic-gate  * It does not even assume that there is any implementation of a
11850Sstevel@tonic-gate  * 32 x 32 --> 64 bit multiply that is accessible to C code and
11860Sstevel@tonic-gate  * appropriate to use.  It falls constructs 32 x 32 --> 64 bit
11870Sstevel@tonic-gate  * multiplies from 16 x 16 --> 32 bit multiplies.
11880Sstevel@tonic-gate  *
11890Sstevel@tonic-gate  */
11900Sstevel@tonic-gate 
11910Sstevel@tonic-gate #if !defined(PSR_MUL)
11920Sstevel@tonic-gate 
11930Sstevel@tonic-gate #ifdef UMUL64
11940Sstevel@tonic-gate 
11956557Sfr41279 #if (BIG_CHUNK_SIZE == 32)
11966557Sfr41279 
11970Sstevel@tonic-gate #define	UNROLL8
11980Sstevel@tonic-gate 
11990Sstevel@tonic-gate #define	MUL_SET_VEC_ROUND_PREFETCH(R) \
12000Sstevel@tonic-gate 	p = pf * d; \
12018933Sopensolaris@drydog.com 	pf = (uint64_t)a[R + 1]; \
12020Sstevel@tonic-gate 	t = p + cy; \
12030Sstevel@tonic-gate 	r[R] = (uint32_t)t; \
12040Sstevel@tonic-gate 	cy = t >> 32
12050Sstevel@tonic-gate 
12060Sstevel@tonic-gate #define	MUL_SET_VEC_ROUND_NOPREFETCH(R) \
12070Sstevel@tonic-gate 	p = pf * d; \
12080Sstevel@tonic-gate 	t = p + cy; \
12090Sstevel@tonic-gate 	r[R] = (uint32_t)t; \
12100Sstevel@tonic-gate 	cy = t >> 32
12110Sstevel@tonic-gate 
12120Sstevel@tonic-gate #define	MUL_ADD_VEC_ROUND_PREFETCH(R) \
12130Sstevel@tonic-gate 	t = (uint64_t)r[R]; \
12140Sstevel@tonic-gate 	p = pf * d; \
12158933Sopensolaris@drydog.com 	pf = (uint64_t)a[R + 1]; \
12160Sstevel@tonic-gate 	t = p + t + cy; \
12170Sstevel@tonic-gate 	r[R] = (uint32_t)t; \
12180Sstevel@tonic-gate 	cy = t >> 32
12190Sstevel@tonic-gate 
12200Sstevel@tonic-gate #define	MUL_ADD_VEC_ROUND_NOPREFETCH(R) \
12210Sstevel@tonic-gate 	t = (uint64_t)r[R]; \
12220Sstevel@tonic-gate 	p = pf * d; \
12230Sstevel@tonic-gate 	t = p + t + cy; \
12240Sstevel@tonic-gate 	r[R] = (uint32_t)t; \
12250Sstevel@tonic-gate 	cy = t >> 32
12260Sstevel@tonic-gate 
12270Sstevel@tonic-gate #ifdef UNROLL8
12280Sstevel@tonic-gate 
12290Sstevel@tonic-gate #define	UNROLL 8
12300Sstevel@tonic-gate 
12310Sstevel@tonic-gate /*
12320Sstevel@tonic-gate  * r = a * b
12330Sstevel@tonic-gate  * where r and a are vectors; b is a single 32-bit digit
12340Sstevel@tonic-gate  */
12350Sstevel@tonic-gate 
12360Sstevel@tonic-gate uint32_t
big_mul_set_vec(uint32_t * r,uint32_t * a,int len,uint32_t b)12370Sstevel@tonic-gate big_mul_set_vec(uint32_t *r, uint32_t *a, int len, uint32_t b)
12380Sstevel@tonic-gate {
12390Sstevel@tonic-gate 	uint64_t d, pf, p, t, cy;
12400Sstevel@tonic-gate 
12410Sstevel@tonic-gate 	if (len == 0)
12420Sstevel@tonic-gate 		return (0);
12430Sstevel@tonic-gate 	cy = 0;
12440Sstevel@tonic-gate 	d = (uint64_t)b;
12450Sstevel@tonic-gate 	pf = (uint64_t)a[0];
12460Sstevel@tonic-gate 	while (len > UNROLL) {
12470Sstevel@tonic-gate 		MUL_SET_VEC_ROUND_PREFETCH(0);
12480Sstevel@tonic-gate 		MUL_SET_VEC_ROUND_PREFETCH(1);
12490Sstevel@tonic-gate 		MUL_SET_VEC_ROUND_PREFETCH(2);
12500Sstevel@tonic-gate 		MUL_SET_VEC_ROUND_PREFETCH(3);
12510Sstevel@tonic-gate 		MUL_SET_VEC_ROUND_PREFETCH(4);
12520Sstevel@tonic-gate 		MUL_SET_VEC_ROUND_PREFETCH(5);
12530Sstevel@tonic-gate 		MUL_SET_VEC_ROUND_PREFETCH(6);
12540Sstevel@tonic-gate 		MUL_SET_VEC_ROUND_PREFETCH(7);
12550Sstevel@tonic-gate 		r += UNROLL;
12560Sstevel@tonic-gate 		a += UNROLL;
12570Sstevel@tonic-gate 		len -= UNROLL;
12580Sstevel@tonic-gate 	}
12590Sstevel@tonic-gate 	if (len == UNROLL) {
12600Sstevel@tonic-gate 		MUL_SET_VEC_ROUND_PREFETCH(0);
12610Sstevel@tonic-gate 		MUL_SET_VEC_ROUND_PREFETCH(1);
12620Sstevel@tonic-gate 		MUL_SET_VEC_ROUND_PREFETCH(2);
12630Sstevel@tonic-gate 		MUL_SET_VEC_ROUND_PREFETCH(3);
12640Sstevel@tonic-gate 		MUL_SET_VEC_ROUND_PREFETCH(4);
12650Sstevel@tonic-gate 		MUL_SET_VEC_ROUND_PREFETCH(5);
12660Sstevel@tonic-gate 		MUL_SET_VEC_ROUND_PREFETCH(6);
12670Sstevel@tonic-gate 		MUL_SET_VEC_ROUND_NOPREFETCH(7);
12680Sstevel@tonic-gate 		return ((uint32_t)cy);
12690Sstevel@tonic-gate 	}
12700Sstevel@tonic-gate 	while (len > 1) {
12710Sstevel@tonic-gate 		MUL_SET_VEC_ROUND_PREFETCH(0);
12720Sstevel@tonic-gate 		++r;
12730Sstevel@tonic-gate 		++a;
12740Sstevel@tonic-gate 		--len;
12750Sstevel@tonic-gate 	}
12760Sstevel@tonic-gate 	if (len > 0) {
12770Sstevel@tonic-gate 		MUL_SET_VEC_ROUND_NOPREFETCH(0);
12780Sstevel@tonic-gate 	}
12790Sstevel@tonic-gate 	return ((uint32_t)cy);
12800Sstevel@tonic-gate }
12810Sstevel@tonic-gate 
12820Sstevel@tonic-gate /*
12830Sstevel@tonic-gate  * r += a * b
12840Sstevel@tonic-gate  * where r and a are vectors; b is a single 32-bit digit
12850Sstevel@tonic-gate  */
12860Sstevel@tonic-gate 
12870Sstevel@tonic-gate uint32_t
big_mul_add_vec(uint32_t * r,uint32_t * a,int len,uint32_t b)12880Sstevel@tonic-gate big_mul_add_vec(uint32_t *r, uint32_t *a, int len, uint32_t b)
12890Sstevel@tonic-gate {
12900Sstevel@tonic-gate 	uint64_t d, pf, p, t, cy;
12910Sstevel@tonic-gate 
12920Sstevel@tonic-gate 	if (len == 0)
12930Sstevel@tonic-gate 		return (0);
12940Sstevel@tonic-gate 	cy = 0;
12950Sstevel@tonic-gate 	d = (uint64_t)b;
12960Sstevel@tonic-gate 	pf = (uint64_t)a[0];
12970Sstevel@tonic-gate 	while (len > 8) {
12980Sstevel@tonic-gate 		MUL_ADD_VEC_ROUND_PREFETCH(0);
12990Sstevel@tonic-gate 		MUL_ADD_VEC_ROUND_PREFETCH(1);
13000Sstevel@tonic-gate 		MUL_ADD_VEC_ROUND_PREFETCH(2);
13010Sstevel@tonic-gate 		MUL_ADD_VEC_ROUND_PREFETCH(3);
13020Sstevel@tonic-gate 		MUL_ADD_VEC_ROUND_PREFETCH(4);
13030Sstevel@tonic-gate 		MUL_ADD_VEC_ROUND_PREFETCH(5);
13040Sstevel@tonic-gate 		MUL_ADD_VEC_ROUND_PREFETCH(6);
13050Sstevel@tonic-gate 		MUL_ADD_VEC_ROUND_PREFETCH(7);
13060Sstevel@tonic-gate 		r += 8;
13070Sstevel@tonic-gate 		a += 8;
13080Sstevel@tonic-gate 		len -= 8;
13090Sstevel@tonic-gate 	}
13100Sstevel@tonic-gate 	if (len == 8) {
13110Sstevel@tonic-gate 		MUL_ADD_VEC_ROUND_PREFETCH(0);
13120Sstevel@tonic-gate 		MUL_ADD_VEC_ROUND_PREFETCH(1);
13130Sstevel@tonic-gate 		MUL_ADD_VEC_ROUND_PREFETCH(2);
13140Sstevel@tonic-gate 		MUL_ADD_VEC_ROUND_PREFETCH(3);
13150Sstevel@tonic-gate 		MUL_ADD_VEC_ROUND_PREFETCH(4);
13160Sstevel@tonic-gate 		MUL_ADD_VEC_ROUND_PREFETCH(5);
13170Sstevel@tonic-gate 		MUL_ADD_VEC_ROUND_PREFETCH(6);
13180Sstevel@tonic-gate 		MUL_ADD_VEC_ROUND_NOPREFETCH(7);
13190Sstevel@tonic-gate 		return ((uint32_t)cy);
13200Sstevel@tonic-gate 	}
13210Sstevel@tonic-gate 	while (len > 1) {
13220Sstevel@tonic-gate 		MUL_ADD_VEC_ROUND_PREFETCH(0);
13230Sstevel@tonic-gate 		++r;
13240Sstevel@tonic-gate 		++a;
13250Sstevel@tonic-gate 		--len;
13260Sstevel@tonic-gate 	}
13270Sstevel@tonic-gate 	if (len > 0) {
13280Sstevel@tonic-gate 		MUL_ADD_VEC_ROUND_NOPREFETCH(0);
13290Sstevel@tonic-gate 	}
13300Sstevel@tonic-gate 	return ((uint32_t)cy);
13310Sstevel@tonic-gate }
13320Sstevel@tonic-gate #endif /* UNROLL8 */
13330Sstevel@tonic-gate 
13340Sstevel@tonic-gate void
big_sqr_vec(uint32_t * r,uint32_t * a,int len)13350Sstevel@tonic-gate big_sqr_vec(uint32_t *r, uint32_t *a, int len)
13360Sstevel@tonic-gate {
13378933Sopensolaris@drydog.com 	uint32_t	*tr, *ta;
13388933Sopensolaris@drydog.com 	int		tlen, row, col;
13398933Sopensolaris@drydog.com 	uint64_t	p, s, t, t2, cy;
13408933Sopensolaris@drydog.com 	uint32_t	d;
13410Sstevel@tonic-gate 
13420Sstevel@tonic-gate 	tr = r + 1;
13430Sstevel@tonic-gate 	ta = a;
13440Sstevel@tonic-gate 	tlen = len - 1;
13450Sstevel@tonic-gate 	tr[tlen] = big_mul_set_vec(tr, ta + 1, tlen, ta[0]);
13460Sstevel@tonic-gate 	while (--tlen > 0) {
13470Sstevel@tonic-gate 		tr += 2;
13480Sstevel@tonic-gate 		++ta;
13490Sstevel@tonic-gate 		tr[tlen] = big_mul_add_vec(tr, ta + 1, tlen, ta[0]);
13500Sstevel@tonic-gate 	}
13510Sstevel@tonic-gate 	s = (uint64_t)a[0];
13520Sstevel@tonic-gate 	s = s * s;
13530Sstevel@tonic-gate 	r[0] = (uint32_t)s;
13540Sstevel@tonic-gate 	cy = s >> 32;
13550Sstevel@tonic-gate 	p = ((uint64_t)r[1] << 1) + cy;
13560Sstevel@tonic-gate 	r[1] = (uint32_t)p;
13570Sstevel@tonic-gate 	cy = p >> 32;
13580Sstevel@tonic-gate 	row = 1;
13590Sstevel@tonic-gate 	col = 2;
13600Sstevel@tonic-gate 	while (row < len) {
13610Sstevel@tonic-gate 		s = (uint64_t)a[row];
13620Sstevel@tonic-gate 		s = s * s;
13630Sstevel@tonic-gate 		p = (uint64_t)r[col] << 1;
13640Sstevel@tonic-gate 		t = p + s;
13650Sstevel@tonic-gate 		d = (uint32_t)t;
13660Sstevel@tonic-gate 		t2 = (uint64_t)d + cy;
13670Sstevel@tonic-gate 		r[col] = (uint32_t)t2;
13680Sstevel@tonic-gate 		cy = (t >> 32) + (t2 >> 32);
13690Sstevel@tonic-gate 		if (row == len - 1)
13700Sstevel@tonic-gate 			break;
13718933Sopensolaris@drydog.com 		p = ((uint64_t)r[col + 1] << 1) + cy;
13728933Sopensolaris@drydog.com 		r[col + 1] = (uint32_t)p;
13730Sstevel@tonic-gate 		cy = p >> 32;
13740Sstevel@tonic-gate 		++row;
13750Sstevel@tonic-gate 		col += 2;
13760Sstevel@tonic-gate 	}
13778933Sopensolaris@drydog.com 	r[col + 1] = (uint32_t)cy;
13780Sstevel@tonic-gate }
13790Sstevel@tonic-gate 
13806557Sfr41279 #else /* BIG_CHUNK_SIZE == 64 */
13816557Sfr41279 
13826557Sfr41279 /*
13836557Sfr41279  * r = r + a * digit, r and a are vectors of length len
13846557Sfr41279  * returns the carry digit
13856557Sfr41279  */
13866557Sfr41279 BIG_CHUNK_TYPE
big_mul_add_vec(BIG_CHUNK_TYPE * r,BIG_CHUNK_TYPE * a,int len,BIG_CHUNK_TYPE digit)13876557Sfr41279 big_mul_add_vec(BIG_CHUNK_TYPE *r, BIG_CHUNK_TYPE *a, int len,
13886557Sfr41279     BIG_CHUNK_TYPE digit)
13896557Sfr41279 {
13906557Sfr41279 	BIG_CHUNK_TYPE	cy, cy1, retcy, dlow, dhigh;
13916557Sfr41279 	int		i;
13926557Sfr41279 
13936557Sfr41279 	cy1 = 0;
13946557Sfr41279 	dlow = digit & BIG_CHUNK_LOWHALFBITS;
13956557Sfr41279 	dhigh = digit >> (BIG_CHUNK_SIZE / 2);
13966557Sfr41279 	for (i = 0; i < len; i++) {
13976557Sfr41279 		cy = (cy1 >> (BIG_CHUNK_SIZE / 2)) +
13986557Sfr41279 		    dlow * (a[i] & BIG_CHUNK_LOWHALFBITS) +
13996557Sfr41279 		    (r[i] & BIG_CHUNK_LOWHALFBITS);
14006557Sfr41279 		cy1 = (cy >> (BIG_CHUNK_SIZE / 2)) +
14016557Sfr41279 		    dlow * (a[i] >> (BIG_CHUNK_SIZE / 2)) +
14026557Sfr41279 		    (r[i] >> (BIG_CHUNK_SIZE / 2));
14036557Sfr41279 		r[i] = (cy & BIG_CHUNK_LOWHALFBITS) |
14046557Sfr41279 		    (cy1 << (BIG_CHUNK_SIZE / 2));
14056557Sfr41279 	}
14066557Sfr41279 	retcy = cy1 >> (BIG_CHUNK_SIZE / 2);
14076557Sfr41279 
14086557Sfr41279 	cy1 = r[0] & BIG_CHUNK_LOWHALFBITS;
14096557Sfr41279 	for (i = 0; i < len - 1; i++) {
14106557Sfr41279 		cy = (cy1 >> (BIG_CHUNK_SIZE / 2)) +
14116557Sfr41279 		    dhigh * (a[i] & BIG_CHUNK_LOWHALFBITS) +
14126557Sfr41279 		    (r[i] >> (BIG_CHUNK_SIZE / 2));
14136557Sfr41279 		r[i] = (cy1 & BIG_CHUNK_LOWHALFBITS) |
14146557Sfr41279 		    (cy << (BIG_CHUNK_SIZE / 2));
14156557Sfr41279 		cy1 = (cy >> (BIG_CHUNK_SIZE / 2)) +
14166557Sfr41279 		    dhigh * (a[i] >> (BIG_CHUNK_SIZE / 2)) +
14176557Sfr41279 		    (r[i + 1] & BIG_CHUNK_LOWHALFBITS);
14186557Sfr41279 	}
14196557Sfr41279 	cy = (cy1 >> (BIG_CHUNK_SIZE / 2)) +
14206557Sfr41279 	    dhigh * (a[len - 1] & BIG_CHUNK_LOWHALFBITS) +
14216557Sfr41279 	    (r[len - 1] >> (BIG_CHUNK_SIZE / 2));
14226557Sfr41279 	r[len - 1] = (cy1 & BIG_CHUNK_LOWHALFBITS) |
14236557Sfr41279 	    (cy << (BIG_CHUNK_SIZE / 2));
14246557Sfr41279 	retcy = (cy >> (BIG_CHUNK_SIZE / 2)) +
14256557Sfr41279 	    dhigh * (a[len - 1] >> (BIG_CHUNK_SIZE / 2)) + retcy;
14266557Sfr41279 
14276557Sfr41279 	return (retcy);
14286557Sfr41279 }
14296557Sfr41279 
14306557Sfr41279 
14316557Sfr41279 /*
14326557Sfr41279  * r = a * digit, r and a are vectors of length len
14336557Sfr41279  * returns the carry digit
14346557Sfr41279  */
14356557Sfr41279 BIG_CHUNK_TYPE
big_mul_set_vec(BIG_CHUNK_TYPE * r,BIG_CHUNK_TYPE * a,int len,BIG_CHUNK_TYPE digit)14366557Sfr41279 big_mul_set_vec(BIG_CHUNK_TYPE *r, BIG_CHUNK_TYPE *a, int len,
14376557Sfr41279     BIG_CHUNK_TYPE digit)
14386557Sfr41279 {
14396557Sfr41279 	int	i;
14406557Sfr41279 
14416557Sfr41279 	ASSERT(r != a);
14426557Sfr41279 	for (i = 0; i < len; i++) {
14436557Sfr41279 		r[i] = 0;
14446557Sfr41279 	}
14456557Sfr41279 	return (big_mul_add_vec(r, a, len, digit));
14466557Sfr41279 }
14476557Sfr41279 
14486557Sfr41279 void
big_sqr_vec(BIG_CHUNK_TYPE * r,BIG_CHUNK_TYPE * a,int len)14496557Sfr41279 big_sqr_vec(BIG_CHUNK_TYPE *r, BIG_CHUNK_TYPE *a, int len)
14506557Sfr41279 {
14516557Sfr41279 	int i;
14526557Sfr41279 
14536557Sfr41279 	ASSERT(r != a);
14546557Sfr41279 	r[len] = big_mul_set_vec(r, a, len, a[0]);
14556557Sfr41279 	for (i = 1; i < len; ++i)
14568933Sopensolaris@drydog.com 		r[len + i] = big_mul_add_vec(r + i, a, len, a[i]);
14576557Sfr41279 }
14586557Sfr41279 
14596557Sfr41279 #endif /* BIG_CHUNK_SIZE == 32/64 */
14606557Sfr41279 
14618933Sopensolaris@drydog.com 
14620Sstevel@tonic-gate #else /* ! UMUL64 */
14630Sstevel@tonic-gate 
14646557Sfr41279 #if (BIG_CHUNK_SIZE != 32)
14656557Sfr41279 #error Don't use 64-bit chunks without defining UMUL64
14666557Sfr41279 #endif
14676557Sfr41279 
14686557Sfr41279 
14690Sstevel@tonic-gate /*
14700Sstevel@tonic-gate  * r = r + a * digit, r and a are vectors of length len
14710Sstevel@tonic-gate  * returns the carry digit
14720Sstevel@tonic-gate  */
14730Sstevel@tonic-gate uint32_t
big_mul_add_vec(uint32_t * r,uint32_t * a,int len,uint32_t digit)14740Sstevel@tonic-gate big_mul_add_vec(uint32_t *r, uint32_t *a, int len, uint32_t digit)
14750Sstevel@tonic-gate {
14760Sstevel@tonic-gate 	uint32_t cy, cy1, retcy, dlow, dhigh;
14770Sstevel@tonic-gate 	int i;
14780Sstevel@tonic-gate 
14790Sstevel@tonic-gate 	cy1 = 0;
14800Sstevel@tonic-gate 	dlow = digit & 0xffff;
14810Sstevel@tonic-gate 	dhigh = digit >> 16;
14820Sstevel@tonic-gate 	for (i = 0; i < len; i++) {
14830Sstevel@tonic-gate 		cy = (cy1 >> 16) + dlow * (a[i] & 0xffff) + (r[i] & 0xffff);
14840Sstevel@tonic-gate 		cy1 = (cy >> 16) + dlow * (a[i]>>16) + (r[i] >> 16);
14850Sstevel@tonic-gate 		r[i] = (cy & 0xffff) | (cy1 << 16);
14860Sstevel@tonic-gate 	}
14870Sstevel@tonic-gate 	retcy = cy1 >> 16;
14880Sstevel@tonic-gate 
14890Sstevel@tonic-gate 	cy1 = r[0] & 0xffff;
14900Sstevel@tonic-gate 	for (i = 0; i < len - 1; i++) {
14910Sstevel@tonic-gate 		cy = (cy1 >> 16) + dhigh * (a[i] & 0xffff) + (r[i] >> 16);
14920Sstevel@tonic-gate 		r[i] = (cy1 & 0xffff) | (cy << 16);
14930Sstevel@tonic-gate 		cy1 = (cy >> 16) + dhigh * (a[i] >> 16) + (r[i + 1] & 0xffff);
14940Sstevel@tonic-gate 	}
14950Sstevel@tonic-gate 	cy = (cy1 >> 16) + dhigh * (a[len - 1] & 0xffff) + (r[len - 1] >> 16);
14960Sstevel@tonic-gate 	r[len - 1] = (cy1 & 0xffff) | (cy << 16);
14970Sstevel@tonic-gate 	retcy = (cy >> 16) + dhigh * (a[len - 1] >> 16) + retcy;
14980Sstevel@tonic-gate 
14990Sstevel@tonic-gate 	return (retcy);
15000Sstevel@tonic-gate }
15010Sstevel@tonic-gate 
15026557Sfr41279 
15030Sstevel@tonic-gate /*
15040Sstevel@tonic-gate  * r = a * digit, r and a are vectors of length len
15050Sstevel@tonic-gate  * returns the carry digit
15060Sstevel@tonic-gate  */
15070Sstevel@tonic-gate uint32_t
15080Sstevel@tonic-gate big_mul_set_vec(uint32_t *r, uint32_t *a, int len, uint32_t digit)
15090Sstevel@tonic-gate {
15106557Sfr41279 	int	i;
15116557Sfr41279 
15126557Sfr41279 	ASSERT(r != a);
15136557Sfr41279 	for (i = 0; i < len; i++) {
15146557Sfr41279 		r[i] = 0;
15156557Sfr41279 	}
15166557Sfr41279 
15170Sstevel@tonic-gate 	return (big_mul_add_vec(r, a, len, digit));
15180Sstevel@tonic-gate }
15190Sstevel@tonic-gate 
15200Sstevel@tonic-gate void
15210Sstevel@tonic-gate big_sqr_vec(uint32_t *r, uint32_t *a, int len)
15220Sstevel@tonic-gate {
15230Sstevel@tonic-gate 	int i;
15240Sstevel@tonic-gate 
15256557Sfr41279 	ASSERT(r != a);
15260Sstevel@tonic-gate 	r[len] = big_mul_set_vec(r, a, len, a[0]);
15270Sstevel@tonic-gate 	for (i = 1; i < len; ++i)
15288933Sopensolaris@drydog.com 		r[len + i] = big_mul_add_vec(r + i, a, len, a[i]);
15290Sstevel@tonic-gate }
15300Sstevel@tonic-gate 
15310Sstevel@tonic-gate #endif /* UMUL64 */
15320Sstevel@tonic-gate 
15330Sstevel@tonic-gate void
15346557Sfr41279 big_mul_vec(BIG_CHUNK_TYPE *r, BIG_CHUNK_TYPE *a, int alen,
15356557Sfr41279     BIG_CHUNK_TYPE *b, int blen)
15360Sstevel@tonic-gate {
15370Sstevel@tonic-gate 	int i;
15380Sstevel@tonic-gate 
15390Sstevel@tonic-gate 	r[alen] = big_mul_set_vec(r, a, alen, b[0]);
15400Sstevel@tonic-gate 	for (i = 1; i < blen; ++i)
15418933Sopensolaris@drydog.com 		r[alen + i] = big_mul_add_vec(r + i, a, alen, b[i]);
15420Sstevel@tonic-gate }
15430Sstevel@tonic-gate 
15440Sstevel@tonic-gate 
15450Sstevel@tonic-gate #endif /* ! PSR_MUL */
15460Sstevel@tonic-gate 
15470Sstevel@tonic-gate 
15480Sstevel@tonic-gate /*
15490Sstevel@tonic-gate  * result = aa * bb  result->value should be big enough to hold the result
15500Sstevel@tonic-gate  *
15510Sstevel@tonic-gate  * Implementation: Standard grammar school algorithm
15520Sstevel@tonic-gate  *
15530Sstevel@tonic-gate  */
15540Sstevel@tonic-gate BIG_ERR_CODE
15550Sstevel@tonic-gate big_mul(BIGNUM *result, BIGNUM *aa, BIGNUM *bb)
15560Sstevel@tonic-gate {
15576557Sfr41279 	BIGNUM		tmp1;
15586557Sfr41279 	BIG_CHUNK_TYPE	tmp1value[BIGTMPSIZE];
15596557Sfr41279 	BIG_CHUNK_TYPE	*r, *t, *a, *b;
15606557Sfr41279 	BIG_ERR_CODE	err;
15616557Sfr41279 	int		i, alen, blen, rsize, sign, diff;
15620Sstevel@tonic-gate 
15630Sstevel@tonic-gate 	if (aa == bb) {
15640Sstevel@tonic-gate 		diff = 0;
15650Sstevel@tonic-gate 	} else {
15660Sstevel@tonic-gate 		diff = big_cmp_abs(aa, bb);
15670Sstevel@tonic-gate 		if (diff < 0) {
15680Sstevel@tonic-gate 			BIGNUM *tt;
15690Sstevel@tonic-gate 			tt = aa;
15700Sstevel@tonic-gate 			aa = bb;
15710Sstevel@tonic-gate 			bb = tt;
15720Sstevel@tonic-gate 		}
15730Sstevel@tonic-gate 	}
15740Sstevel@tonic-gate 	a = aa->value;
15750Sstevel@tonic-gate 	b = bb->value;
15760Sstevel@tonic-gate 	alen = aa->len;
15770Sstevel@tonic-gate 	blen = bb->len;
15786557Sfr41279 	while ((alen > 1) && (a[alen - 1] == 0)) {
15796557Sfr41279 		alen--;
15806557Sfr41279 	}
15810Sstevel@tonic-gate 	aa->len = alen;
15826557Sfr41279 	while ((blen > 1) && (b[blen - 1] == 0)) {
15836557Sfr41279 		blen--;
15846557Sfr41279 	}
15850Sstevel@tonic-gate 	bb->len = blen;
15860Sstevel@tonic-gate 
15870Sstevel@tonic-gate 	rsize = alen + blen;
15888933Sopensolaris@drydog.com 	ASSERT(rsize > 0);
15890Sstevel@tonic-gate 	if (result->size < rsize) {
15900Sstevel@tonic-gate 		err = big_extend(result, rsize);
15916557Sfr41279 		if (err != BIG_OK) {
15920Sstevel@tonic-gate 			return (err);
15936557Sfr41279 		}
15940Sstevel@tonic-gate 		/* aa or bb might be an alias to result */
15950Sstevel@tonic-gate 		a = aa->value;
15960Sstevel@tonic-gate 		b = bb->value;
15970Sstevel@tonic-gate 	}
15980Sstevel@tonic-gate 	r = result->value;
15990Sstevel@tonic-gate 
16000Sstevel@tonic-gate 	if (((alen == 1) && (a[0] == 0)) || ((blen == 1) && (b[0] == 0))) {
16010Sstevel@tonic-gate 		result->len = 1;
16020Sstevel@tonic-gate 		result->sign = 1;
16030Sstevel@tonic-gate 		r[0] = 0;
16040Sstevel@tonic-gate 		return (BIG_OK);
16050Sstevel@tonic-gate 	}
16060Sstevel@tonic-gate 	sign = aa->sign * bb->sign;
16070Sstevel@tonic-gate 	if ((alen == 1) && (a[0] == 1)) {
16086557Sfr41279 		for (i = 0; i < blen; i++) {
16096557Sfr41279 			r[i] = b[i];
16106557Sfr41279 		}
16110Sstevel@tonic-gate 		result->len = blen;
16120Sstevel@tonic-gate 		result->sign = sign;
16130Sstevel@tonic-gate 		return (BIG_OK);
16140Sstevel@tonic-gate 	}
16150Sstevel@tonic-gate 	if ((blen == 1) && (b[0] == 1)) {
16166557Sfr41279 		for (i = 0; i < alen; i++) {
16176557Sfr41279 			r[i] = a[i];
16186557Sfr41279 		}
16190Sstevel@tonic-gate 		result->len = alen;
16200Sstevel@tonic-gate 		result->sign = sign;
16210Sstevel@tonic-gate 		return (BIG_OK);
16220Sstevel@tonic-gate 	}
16230Sstevel@tonic-gate 
16246557Sfr41279 	if ((err = big_init1(&tmp1, rsize,
16256557Sfr41279 	    tmp1value, arraysize(tmp1value))) != BIG_OK) {
16260Sstevel@tonic-gate 		return (err);
16276557Sfr41279 	}
16280Sstevel@tonic-gate 	t = tmp1.value;
16296557Sfr41279 
16306557Sfr41279 	for (i = 0; i < rsize; i++) {
16316557Sfr41279 		t[i] = 0;
16326557Sfr41279 	}
16336557Sfr41279 
16346557Sfr41279 	if (diff == 0 && alen > 2) {
16350Sstevel@tonic-gate 		BIG_SQR_VEC(t, a, alen);
16366557Sfr41279 	} else if (blen > 0) {
16370Sstevel@tonic-gate 		BIG_MUL_VEC(t, a, alen, b, blen);
16386557Sfr41279 	}
16396557Sfr41279 
16406557Sfr41279 	if (t[rsize - 1] == 0) {
16416557Sfr41279 		tmp1.len = rsize - 1;
16426557Sfr41279 	} else {
16436557Sfr41279 		tmp1.len = rsize;
16446557Sfr41279 	}
16458933Sopensolaris@drydog.com 
16468933Sopensolaris@drydog.com 	err = big_copy(result, &tmp1);
16478933Sopensolaris@drydog.com 
16480Sstevel@tonic-gate 	result->sign = sign;
16490Sstevel@tonic-gate 
16506557Sfr41279 	big_finish(&tmp1);
16510Sstevel@tonic-gate 
16528933Sopensolaris@drydog.com 	return (err);
16530Sstevel@tonic-gate }
16540Sstevel@tonic-gate 
16550Sstevel@tonic-gate 
16560Sstevel@tonic-gate /*
16579302Sopensolaris@drydog.com  * big_mont_mul()
16589302Sopensolaris@drydog.com  * Montgomery multiplication.
16599302Sopensolaris@drydog.com  *
16609302Sopensolaris@drydog.com  * Caller must ensure that  a < n,  b < n,  ret->size >=  2 * n->len + 1,
16619302Sopensolaris@drydog.com  * and that ret is not n.
16620Sstevel@tonic-gate  */
16630Sstevel@tonic-gate BIG_ERR_CODE
16646557Sfr41279 big_mont_mul(BIGNUM *ret, BIGNUM *a, BIGNUM *b, BIGNUM *n, BIG_CHUNK_TYPE n0)
16650Sstevel@tonic-gate {
16669302Sopensolaris@drydog.com 	int		i, j, nlen, needsubtract;
16679302Sopensolaris@drydog.com 	BIG_CHUNK_TYPE	*nn, *rr, *rrplusi;
16686557Sfr41279 	BIG_CHUNK_TYPE	digit, c;
16696557Sfr41279 	BIG_ERR_CODE	err;
16709302Sopensolaris@drydog.com #ifdef	__amd64
16719302Sopensolaris@drydog.com #define	BIG_CPU_UNKNOWN	0
16729302Sopensolaris@drydog.com #define	BIG_CPU_AMD	1
16739302Sopensolaris@drydog.com #define	BIG_CPU_INTEL	2
16749302Sopensolaris@drydog.com 	static int	big_cpu = BIG_CPU_UNKNOWN;
16759302Sopensolaris@drydog.com 	BIG_CHUNK_TYPE	carry[BIGTMPSIZE];
16769302Sopensolaris@drydog.com 
16779302Sopensolaris@drydog.com 	if (big_cpu == BIG_CPU_UNKNOWN) {
16789302Sopensolaris@drydog.com 		big_cpu = 1 + bignum_on_intel();
16799302Sopensolaris@drydog.com 	}
16809302Sopensolaris@drydog.com #endif	/* __amd64 */
16810Sstevel@tonic-gate 
16820Sstevel@tonic-gate 	nlen = n->len;
16830Sstevel@tonic-gate 	nn = n->value;
16840Sstevel@tonic-gate 
16850Sstevel@tonic-gate 	rr = ret->value;
16860Sstevel@tonic-gate 
16876557Sfr41279 	if ((err = big_mul(ret, a, b)) != BIG_OK) {
16880Sstevel@tonic-gate 		return (err);
16896557Sfr41279 	}
16900Sstevel@tonic-gate 
16910Sstevel@tonic-gate 	rr = ret->value;
16926557Sfr41279 	for (i = ret->len; i < 2 * nlen + 1; i++) {
16936557Sfr41279 		rr[i] = 0;
16946557Sfr41279 	}
16959302Sopensolaris@drydog.com 
16969302Sopensolaris@drydog.com #ifdef	__amd64	/* pipelining optimization for Intel 64, but not AMD64 */
16979302Sopensolaris@drydog.com 	if ((big_cpu == BIG_CPU_INTEL) && (nlen <= BIGTMPSIZE)) {
16989302Sopensolaris@drydog.com 		/*
16999302Sopensolaris@drydog.com 		 * Perform the following in two for loops to reduce the
17009302Sopensolaris@drydog.com 		 * dependency between computing the carryover bits with
17019302Sopensolaris@drydog.com 		 * BIG_MUL_ADD_VEC() and adding them, thus improving pipelining.
17029302Sopensolaris@drydog.com 		 */
17039302Sopensolaris@drydog.com 		for (i = 0; i < nlen; i++) {
17049302Sopensolaris@drydog.com 			rrplusi = rr + i;
17059302Sopensolaris@drydog.com 			digit = *rrplusi * n0;
17069302Sopensolaris@drydog.com 			carry[i] = BIG_MUL_ADD_VEC(rrplusi, nn, nlen, digit);
17079302Sopensolaris@drydog.com 		}
17089302Sopensolaris@drydog.com 		for (i = 0; i < nlen; i++) {
17099302Sopensolaris@drydog.com 			j = i + nlen;
17109302Sopensolaris@drydog.com 			rr[j] += carry[i];
17119302Sopensolaris@drydog.com 			while (rr[j] < carry[i]) {
17129302Sopensolaris@drydog.com 				rr[++j] += 1;
17139302Sopensolaris@drydog.com 				carry[i] = 1;
17149302Sopensolaris@drydog.com 			}
17159302Sopensolaris@drydog.com 		}
17169302Sopensolaris@drydog.com 	} else
17179302Sopensolaris@drydog.com #endif	/* __amd64 */
17189302Sopensolaris@drydog.com 	{ /* no pipelining optimization */
17199302Sopensolaris@drydog.com 		for (i = 0; i < nlen; i++) {
17209302Sopensolaris@drydog.com 			rrplusi = rr + i;
17219302Sopensolaris@drydog.com 			digit = *rrplusi * n0;
17229302Sopensolaris@drydog.com 			c = BIG_MUL_ADD_VEC(rrplusi, nn, nlen, digit);
17239302Sopensolaris@drydog.com 			j = i + nlen;
17249302Sopensolaris@drydog.com 			rr[j] += c;
17259302Sopensolaris@drydog.com 			while (rr[j] < c) {
17269302Sopensolaris@drydog.com 				rr[++j] += 1;
17279302Sopensolaris@drydog.com 				c = 1;
17289302Sopensolaris@drydog.com 			}
17290Sstevel@tonic-gate 		}
17300Sstevel@tonic-gate 	}
17310Sstevel@tonic-gate 
17320Sstevel@tonic-gate 	needsubtract = 0;
17330Sstevel@tonic-gate 	if ((rr[2 * nlen]  != 0))
17340Sstevel@tonic-gate 		needsubtract = 1;
17350Sstevel@tonic-gate 	else {
17360Sstevel@tonic-gate 		for (i = 2 * nlen - 1; i >= nlen; i--) {
17370Sstevel@tonic-gate 			if (rr[i] > nn[i - nlen]) {
17380Sstevel@tonic-gate 				needsubtract = 1;
17390Sstevel@tonic-gate 				break;
17406557Sfr41279 			} else if (rr[i] < nn[i - nlen]) {
17416557Sfr41279 				break;
17426557Sfr41279 			}
17430Sstevel@tonic-gate 		}
17440Sstevel@tonic-gate 	}
17450Sstevel@tonic-gate 	if (needsubtract)
17460Sstevel@tonic-gate 		big_sub_vec(rr, rr + nlen, nn, nlen);
17470Sstevel@tonic-gate 	else {
17486557Sfr41279 		for (i = 0; i < nlen; i++) {
17490Sstevel@tonic-gate 			rr[i] = rr[i + nlen];
17506557Sfr41279 		}
17510Sstevel@tonic-gate 	}
17528933Sopensolaris@drydog.com 
17538933Sopensolaris@drydog.com 	/* Remove leading zeros, but keep at least 1 digit: */
17548933Sopensolaris@drydog.com 	for (i = nlen - 1; (i > 0) && (rr[i] == 0); i--)
17556557Sfr41279 		;
17568933Sopensolaris@drydog.com 	ret->len = i + 1;
17570Sstevel@tonic-gate 
17580Sstevel@tonic-gate 	return (BIG_OK);
17590Sstevel@tonic-gate }
17600Sstevel@tonic-gate 
17616557Sfr41279 
17626557Sfr41279 BIG_CHUNK_TYPE
17636557Sfr41279 big_n0(BIG_CHUNK_TYPE n)
17640Sstevel@tonic-gate {
17656557Sfr41279 	int		i;
17666557Sfr41279 	BIG_CHUNK_TYPE	result, tmp;
17670Sstevel@tonic-gate 
17680Sstevel@tonic-gate 	result = 0;
17696557Sfr41279 	tmp = BIG_CHUNK_ALLBITS;
17706557Sfr41279 	for (i = 0; i < BIG_CHUNK_SIZE; i++) {
17710Sstevel@tonic-gate 		if ((tmp & 1) == 1) {
17726557Sfr41279 			result = (result >> 1) | BIG_CHUNK_HIGHBIT;
17730Sstevel@tonic-gate 			tmp = tmp - n;
17746557Sfr41279 		} else {
17756557Sfr41279 			result = (result >> 1);
17766557Sfr41279 		}
17770Sstevel@tonic-gate 		tmp = tmp >> 1;
17780Sstevel@tonic-gate 	}
17790Sstevel@tonic-gate 
17800Sstevel@tonic-gate 	return (result);
17810Sstevel@tonic-gate }
17820Sstevel@tonic-gate 
17830Sstevel@tonic-gate 
17840Sstevel@tonic-gate int
17850Sstevel@tonic-gate big_numbits(BIGNUM *n)
17860Sstevel@tonic-gate {
17876557Sfr41279 	int		i, j;
17886557Sfr41279 	BIG_CHUNK_TYPE	t;
17896557Sfr41279 
17906557Sfr41279 	for (i = n->len - 1; i > 0; i--) {
17916557Sfr41279 		if (n->value[i] != 0) {
17926557Sfr41279 			break;
17936557Sfr41279 		}
17946557Sfr41279 	}
17950Sstevel@tonic-gate 	t = n->value[i];
17966557Sfr41279 	for (j = BIG_CHUNK_SIZE; j > 0; j--) {
17976557Sfr41279 		if ((t & BIG_CHUNK_HIGHBIT) == 0) {
17980Sstevel@tonic-gate 			t = t << 1;
17996557Sfr41279 		} else {
18006557Sfr41279 			return (BIG_CHUNK_SIZE * i + j);
18016557Sfr41279 		}
18020Sstevel@tonic-gate 	}
18030Sstevel@tonic-gate 	return (0);
18040Sstevel@tonic-gate }
18050Sstevel@tonic-gate 
18066557Sfr41279 
18070Sstevel@tonic-gate /* caller must make sure that a < n */
18080Sstevel@tonic-gate BIG_ERR_CODE
18090Sstevel@tonic-gate big_mont_rr(BIGNUM *result, BIGNUM *n)
18100Sstevel@tonic-gate {
18116557Sfr41279 	BIGNUM		rr;
18126557Sfr41279 	BIG_CHUNK_TYPE	rrvalue[BIGTMPSIZE];
18136557Sfr41279 	int		len, i;
18146557Sfr41279 	BIG_ERR_CODE	err;
18150Sstevel@tonic-gate 
18160Sstevel@tonic-gate 	rr.malloced = 0;
18170Sstevel@tonic-gate 	len = n->len;
18180Sstevel@tonic-gate 
18190Sstevel@tonic-gate 	if ((err = big_init1(&rr, 2 * len + 1,
18206557Sfr41279 	    rrvalue, arraysize(rrvalue))) != BIG_OK) {
18210Sstevel@tonic-gate 		return (err);
18226557Sfr41279 	}
18236557Sfr41279 
18246557Sfr41279 	for (i = 0; i < 2 * len; i++) {
18256557Sfr41279 		rr.value[i] = 0;
18266557Sfr41279 	}
18270Sstevel@tonic-gate 	rr.value[2 * len] = 1;
18280Sstevel@tonic-gate 	rr.len = 2 * len + 1;
18296557Sfr41279 	if ((err = big_div_pos(NULL, &rr, &rr, n)) != BIG_OK) {
18300Sstevel@tonic-gate 		goto ret;
18316557Sfr41279 	}
18320Sstevel@tonic-gate 	err = big_copy(result, &rr);
18330Sstevel@tonic-gate ret:
18346557Sfr41279 	big_finish(&rr);
18350Sstevel@tonic-gate 	return (err);
18360Sstevel@tonic-gate }
18370Sstevel@tonic-gate 
18386557Sfr41279 
18390Sstevel@tonic-gate /* caller must make sure that a < n */
18400Sstevel@tonic-gate BIG_ERR_CODE
18416557Sfr41279 big_mont_conv(BIGNUM *result, BIGNUM *a, BIGNUM *n, BIG_CHUNK_TYPE n0,
18426557Sfr41279     BIGNUM *n_rr)
18430Sstevel@tonic-gate {
18446557Sfr41279 	BIGNUM		rr;
18456557Sfr41279 	BIG_CHUNK_TYPE	rrvalue[BIGTMPSIZE];
18466557Sfr41279 	int		len, i;
18476557Sfr41279 	BIG_ERR_CODE	err;
18480Sstevel@tonic-gate 
18490Sstevel@tonic-gate 	rr.malloced = 0;
18500Sstevel@tonic-gate 	len = n->len;
18510Sstevel@tonic-gate 
18520Sstevel@tonic-gate 	if ((err = big_init1(&rr, 2 * len + 1, rrvalue, arraysize(rrvalue)))
18536557Sfr41279 	    != BIG_OK) {
18546557Sfr41279 		return (err);
18556557Sfr41279 	}
18560Sstevel@tonic-gate 
18570Sstevel@tonic-gate 	if (n_rr == NULL) {
18586557Sfr41279 		for (i = 0; i < 2 * len; i++) {
18596557Sfr41279 			rr.value[i] = 0;
18606557Sfr41279 		}
18610Sstevel@tonic-gate 		rr.value[2 * len] = 1;
18620Sstevel@tonic-gate 		rr.len = 2 * len + 1;
18636557Sfr41279 		if ((err = big_div_pos(NULL, &rr, &rr, n)) != BIG_OK) {
18640Sstevel@tonic-gate 			goto ret;
18656557Sfr41279 		}
18660Sstevel@tonic-gate 		n_rr = &rr;
18670Sstevel@tonic-gate 	}
18680Sstevel@tonic-gate 
18696557Sfr41279 	if ((err = big_mont_mul(&rr, n_rr, a, n, n0)) != BIG_OK) {
18700Sstevel@tonic-gate 		goto ret;
18716557Sfr41279 	}
18720Sstevel@tonic-gate 	err = big_copy(result, &rr);
18736557Sfr41279 
18740Sstevel@tonic-gate ret:
18756557Sfr41279 	big_finish(&rr);
18760Sstevel@tonic-gate 	return (err);
18770Sstevel@tonic-gate }
18780Sstevel@tonic-gate 
18790Sstevel@tonic-gate 
18806557Sfr41279 #ifdef	USE_FLOATING_POINT
18816557Sfr41279 #define	big_modexp_ncp_float	big_modexp_ncp_sw
18826557Sfr41279 #else
18836557Sfr41279 #define	big_modexp_ncp_int	big_modexp_ncp_sw
18846557Sfr41279 #endif
18856557Sfr41279 
18860Sstevel@tonic-gate #define	MAX_EXP_BIT_GROUP_SIZE 6
18870Sstevel@tonic-gate #define	APOWERS_MAX_SIZE (1 << (MAX_EXP_BIT_GROUP_SIZE - 1))
18880Sstevel@tonic-gate 
18896557Sfr41279 /* ARGSUSED */
18900Sstevel@tonic-gate static BIG_ERR_CODE
18916557Sfr41279 big_modexp_ncp_int(BIGNUM *result, BIGNUM *ma, BIGNUM *e, BIGNUM *n,
18926557Sfr41279     BIGNUM *tmp, BIG_CHUNK_TYPE n0)
18936557Sfr41279 
18940Sstevel@tonic-gate {
18956557Sfr41279 	BIGNUM		apowers[APOWERS_MAX_SIZE];
18966557Sfr41279 	BIGNUM		tmp1;
18976557Sfr41279 	BIG_CHUNK_TYPE	tmp1value[BIGTMPSIZE];
18986557Sfr41279 	int		i, j, k, l, m, p;
18998933Sopensolaris@drydog.com 	uint32_t	bit, bitind, bitcount, groupbits, apowerssize;
19008933Sopensolaris@drydog.com 	uint32_t	nbits;
19016557Sfr41279 	BIG_ERR_CODE	err;
19020Sstevel@tonic-gate 
19030Sstevel@tonic-gate 	nbits = big_numbits(e);
19040Sstevel@tonic-gate 	if (nbits < 50) {
19050Sstevel@tonic-gate 		groupbits = 1;
19060Sstevel@tonic-gate 		apowerssize = 1;
19070Sstevel@tonic-gate 	} else {
19080Sstevel@tonic-gate 		groupbits = MAX_EXP_BIT_GROUP_SIZE;
19090Sstevel@tonic-gate 		apowerssize = 1 << (groupbits - 1);
19100Sstevel@tonic-gate 	}
19110Sstevel@tonic-gate 
19126557Sfr41279 
19136557Sfr41279 	if ((err = big_init1(&tmp1, 2 * n->len + 1,
19146557Sfr41279 	    tmp1value, arraysize(tmp1value))) != BIG_OK) {
19150Sstevel@tonic-gate 		return (err);
19166557Sfr41279 	}
19176557Sfr41279 
19188933Sopensolaris@drydog.com 	/* clear the malloced bit to help cleanup */
19196557Sfr41279 	for (i = 0; i < apowerssize; i++) {
19206557Sfr41279 		apowers[i].malloced = 0;
19216557Sfr41279 	}
19228933Sopensolaris@drydog.com 
19236557Sfr41279 	for (i = 0; i < apowerssize; i++) {
19246557Sfr41279 		if ((err = big_init1(&(apowers[i]), n->len, NULL, 0)) !=
19256557Sfr41279 		    BIG_OK) {
19266557Sfr41279 			goto ret;
19276557Sfr41279 		}
19286557Sfr41279 	}
19296557Sfr41279 
19306557Sfr41279 	(void) big_copy(&(apowers[0]), ma);
19316557Sfr41279 
19326557Sfr41279 	if ((err = big_mont_mul(&tmp1, ma, ma, n, n0)) != BIG_OK) {
19336557Sfr41279 		goto ret;
19346557Sfr41279 	}
19356557Sfr41279 	(void) big_copy(ma, &tmp1);
19366557Sfr41279 
19376557Sfr41279 	for (i = 1; i < apowerssize; i++) {
19386557Sfr41279 		if ((err = big_mont_mul(&tmp1, ma,
19398933Sopensolaris@drydog.com 		    &(apowers[i - 1]), n, n0)) != BIG_OK) {
19406557Sfr41279 			goto ret;
19416557Sfr41279 		}
19426557Sfr41279 		(void) big_copy(&apowers[i], &tmp1);
19436557Sfr41279 	}
19446557Sfr41279 
19456557Sfr41279 	bitind = nbits % BIG_CHUNK_SIZE;
19466557Sfr41279 	k = 0;
19476557Sfr41279 	l = 0;
19486557Sfr41279 	p = 0;
19496557Sfr41279 	bitcount = 0;
19506557Sfr41279 	for (i = nbits / BIG_CHUNK_SIZE; i >= 0; i--) {
19516557Sfr41279 		for (j = bitind - 1; j >= 0; j--) {
19526557Sfr41279 			bit = (e->value[i] >> j) & 1;
19536557Sfr41279 			if ((bitcount == 0) && (bit == 0)) {
19546557Sfr41279 				if ((err = big_mont_mul(tmp,
19556557Sfr41279 				    tmp, tmp, n, n0)) != BIG_OK) {
19566557Sfr41279 					goto ret;
19576557Sfr41279 				}
19586557Sfr41279 			} else {
19596557Sfr41279 				bitcount++;
19606557Sfr41279 				p = p * 2 + bit;
19616557Sfr41279 				if (bit == 1) {
19626557Sfr41279 					k = k + l + 1;
19636557Sfr41279 					l = 0;
19646557Sfr41279 				} else {
19656557Sfr41279 					l++;
19666557Sfr41279 				}
19676557Sfr41279 				if (bitcount == groupbits) {
19686557Sfr41279 					for (m = 0; m < k; m++) {
19696557Sfr41279 						if ((err = big_mont_mul(tmp,
19706557Sfr41279 						    tmp, tmp, n, n0)) !=
19716557Sfr41279 						    BIG_OK) {
19726557Sfr41279 							goto ret;
19736557Sfr41279 						}
19746557Sfr41279 					}
19756557Sfr41279 					if ((err = big_mont_mul(tmp, tmp,
19766557Sfr41279 					    &(apowers[p >> (l + 1)]),
19776557Sfr41279 					    n, n0)) != BIG_OK) {
19786557Sfr41279 						goto ret;
19796557Sfr41279 					}
19806557Sfr41279 					for (m = 0; m < l; m++) {
19816557Sfr41279 						if ((err = big_mont_mul(tmp,
19826557Sfr41279 						    tmp, tmp, n, n0)) !=
19836557Sfr41279 						    BIG_OK) {
19846557Sfr41279 							goto ret;
19856557Sfr41279 						}
19866557Sfr41279 					}
19876557Sfr41279 					k = 0;
19886557Sfr41279 					l = 0;
19896557Sfr41279 					p = 0;
19906557Sfr41279 					bitcount = 0;
19916557Sfr41279 				}
19926557Sfr41279 			}
19936557Sfr41279 		}
19946557Sfr41279 		bitind = BIG_CHUNK_SIZE;
19950Sstevel@tonic-gate 	}
19960Sstevel@tonic-gate 
19976557Sfr41279 	for (m = 0; m < k; m++) {
19986557Sfr41279 		if ((err = big_mont_mul(tmp, tmp, tmp, n, n0)) != BIG_OK) {
19996557Sfr41279 			goto ret;
20006557Sfr41279 		}
20016557Sfr41279 	}
20026557Sfr41279 	if (p != 0) {
20036557Sfr41279 		if ((err = big_mont_mul(tmp, tmp,
20046557Sfr41279 		    &(apowers[p >> (l + 1)]), n, n0)) != BIG_OK) {
20056557Sfr41279 			goto ret;
20066557Sfr41279 		}
20076557Sfr41279 	}
20086557Sfr41279 	for (m = 0; m < l; m++) {
20096557Sfr41279 		if ((err = big_mont_mul(result, tmp, tmp, n, n0)) != BIG_OK) {
20106557Sfr41279 			goto ret;
20116557Sfr41279 		}
20126557Sfr41279 	}
20136557Sfr41279 
20146557Sfr41279 ret:
20156557Sfr41279 	for (i = apowerssize - 1; i >= 0; i--) {
20166557Sfr41279 		big_finish(&(apowers[i]));
20170Sstevel@tonic-gate 	}
20186557Sfr41279 	big_finish(&tmp1);
20196557Sfr41279 
20206557Sfr41279 	return (err);
20216557Sfr41279 }
20226557Sfr41279 
20236557Sfr41279 
20246557Sfr41279 #ifdef USE_FLOATING_POINT
20256557Sfr41279 
20266557Sfr41279 #ifdef _KERNEL
20276557Sfr41279 
20286557Sfr41279 #include <sys/sysmacros.h>
20296557Sfr41279 #include <sys/regset.h>
20306557Sfr41279 #include <sys/fpu/fpusystm.h>
20316557Sfr41279 
20326557Sfr41279 /* the alignment for block stores to save fp registers */
20336557Sfr41279 #define	FPR_ALIGN	(64)
20346557Sfr41279 
20356557Sfr41279 extern void big_savefp(kfpu_t *);
20366557Sfr41279 extern void big_restorefp(kfpu_t *);
20376557Sfr41279 
20386557Sfr41279 #endif /* _KERNEL */
20396557Sfr41279 
20406557Sfr41279 /*
20416557Sfr41279  * This version makes use of floating point for performance
20426557Sfr41279  */
20436557Sfr41279 static BIG_ERR_CODE
20446557Sfr41279 big_modexp_ncp_float(BIGNUM *result, BIGNUM *ma, BIGNUM *e, BIGNUM *n,
20456557Sfr41279     BIGNUM *tmp, BIG_CHUNK_TYPE n0)
20466557Sfr41279 {
20476557Sfr41279 
20488933Sopensolaris@drydog.com 	int		i, j, k, l, m, p;
20498933Sopensolaris@drydog.com 	uint32_t	bit, bitind, bitcount, nlen;
20506557Sfr41279 	double		dn0;
20516557Sfr41279 	double		*dn, *dt, *d16r, *d32r;
20526557Sfr41279 	uint32_t	*nint, *prod;
20536557Sfr41279 	double		*apowers[APOWERS_MAX_SIZE];
20548933Sopensolaris@drydog.com 	uint32_t	nbits, groupbits, apowerssize;
20556557Sfr41279 	BIG_ERR_CODE	err = BIG_OK;
20566557Sfr41279 
20576557Sfr41279 #ifdef _KERNEL
20586557Sfr41279 	uint8_t fpua[sizeof (kfpu_t) + FPR_ALIGN];
20596557Sfr41279 	kfpu_t *fpu;
20606557Sfr41279 
20616557Sfr41279 #ifdef DEBUG
20626557Sfr41279 	if (!fpu_exists)
20636557Sfr41279 		return (BIG_GENERAL_ERR);
20646557Sfr41279 #endif
20656557Sfr41279 
20666557Sfr41279 	fpu =  (kfpu_t *)P2ROUNDUP((uintptr_t)fpua, FPR_ALIGN);
20676557Sfr41279 	big_savefp(fpu);
20686557Sfr41279 
20696557Sfr41279 #endif /* _KERNEL */
20706557Sfr41279 
20716557Sfr41279 	nbits = big_numbits(e);
20726557Sfr41279 	if (nbits < 50) {
20736557Sfr41279 		groupbits = 1;
20746557Sfr41279 		apowerssize = 1;
20756557Sfr41279 	} else {
20766557Sfr41279 		groupbits = MAX_EXP_BIT_GROUP_SIZE;
20776557Sfr41279 		apowerssize = 1 << (groupbits - 1);
20786557Sfr41279 	}
20796557Sfr41279 
20806557Sfr41279 	nlen = (BIG_CHUNK_SIZE / 32) * n->len;
20810Sstevel@tonic-gate 	dn0 = (double)(n0 & 0xffff);
20820Sstevel@tonic-gate 
20830Sstevel@tonic-gate 	dn = dt = d16r = d32r = NULL;
20840Sstevel@tonic-gate 	nint = prod = NULL;
20850Sstevel@tonic-gate 	for (i = 0; i < apowerssize; i++) {
20860Sstevel@tonic-gate 		apowers[i] = NULL;
20870Sstevel@tonic-gate 	}
20880Sstevel@tonic-gate 
20890Sstevel@tonic-gate 	if ((dn = big_malloc(nlen * sizeof (double))) == NULL) {
20900Sstevel@tonic-gate 		err = BIG_NO_MEM;
20910Sstevel@tonic-gate 		goto ret;
20920Sstevel@tonic-gate 	}
20930Sstevel@tonic-gate 	if ((dt = big_malloc((4 * nlen + 2) * sizeof (double))) == NULL) {
20940Sstevel@tonic-gate 		err = BIG_NO_MEM;
20950Sstevel@tonic-gate 		goto ret;
20960Sstevel@tonic-gate 	}
20970Sstevel@tonic-gate 	if ((nint = big_malloc(nlen * sizeof (uint32_t))) == NULL) {
20980Sstevel@tonic-gate 		err = BIG_NO_MEM;
20990Sstevel@tonic-gate 		goto ret;
21000Sstevel@tonic-gate 	}
21010Sstevel@tonic-gate 	if ((prod = big_malloc((nlen + 1) * sizeof (uint32_t))) == NULL) {
21020Sstevel@tonic-gate 		err = BIG_NO_MEM;
21030Sstevel@tonic-gate 		goto ret;
21040Sstevel@tonic-gate 	}
21050Sstevel@tonic-gate 	if ((d16r = big_malloc((2 * nlen + 1) * sizeof (double))) == NULL) {
21060Sstevel@tonic-gate 		err = BIG_NO_MEM;
21070Sstevel@tonic-gate 		goto ret;
21080Sstevel@tonic-gate 	}
21090Sstevel@tonic-gate 	if ((d32r = big_malloc(nlen * sizeof (double))) == NULL) {
21100Sstevel@tonic-gate 		err = BIG_NO_MEM;
21110Sstevel@tonic-gate 		goto ret;
21120Sstevel@tonic-gate 	}
21130Sstevel@tonic-gate 	for (i = 0; i < apowerssize; i++) {
21140Sstevel@tonic-gate 		if ((apowers[i] = big_malloc((2 * nlen + 1) *
21150Sstevel@tonic-gate 		    sizeof (double))) == NULL) {
21160Sstevel@tonic-gate 			err = BIG_NO_MEM;
21170Sstevel@tonic-gate 			goto ret;
21180Sstevel@tonic-gate 		}
21190Sstevel@tonic-gate 	}
21200Sstevel@tonic-gate 
21216557Sfr41279 #if (BIG_CHUNK_SIZE == 32)
21226557Sfr41279 	for (i = 0; i < ma->len; i++) {
21236557Sfr41279 		nint[i] = ma->value[i];
21246557Sfr41279 	}
21256557Sfr41279 	for (; i < nlen; i++) {
21266557Sfr41279 		nint[i] = 0;
21276557Sfr41279 	}
21286557Sfr41279 #else
21296557Sfr41279 	for (i = 0; i < ma->len; i++) {
21306557Sfr41279 		nint[2 * i] = (uint32_t)(ma->value[i] & 0xffffffffULL);
21316557Sfr41279 		nint[2 * i + 1] = (uint32_t)(ma->value[i] >> 32);
21326557Sfr41279 	}
21336557Sfr41279 	for (i = ma->len * 2; i < nlen; i++) {
21346557Sfr41279 		nint[i] = 0;
21356557Sfr41279 	}
21366557Sfr41279 #endif
21370Sstevel@tonic-gate 	conv_i32_to_d32_and_d16(d32r, apowers[0], nint, nlen);
21380Sstevel@tonic-gate 
21396557Sfr41279 #if (BIG_CHUNK_SIZE == 32)
21406557Sfr41279 	for (i = 0; i < n->len; i++) {
21416557Sfr41279 		nint[i] = n->value[i];
21426557Sfr41279 	}
21436557Sfr41279 	for (; i < nlen; i++) {
21446557Sfr41279 		nint[i] = 0;
21456557Sfr41279 	}
21466557Sfr41279 #else
21476557Sfr41279 	for (i = 0; i < n->len; i++) {
21486557Sfr41279 		nint[2 * i] = (uint32_t)(n->value[i] & 0xffffffffULL);
21496557Sfr41279 		nint[2 * i + 1] = (uint32_t)(n->value[i] >> 32);
21506557Sfr41279 	}
21516557Sfr41279 	for (i = n->len * 2; i < nlen; i++) {
21526557Sfr41279 		nint[i] = 0;
21536557Sfr41279 	}
21546557Sfr41279 #endif
21550Sstevel@tonic-gate 	conv_i32_to_d32(dn, nint, nlen);
21560Sstevel@tonic-gate 
21570Sstevel@tonic-gate 	mont_mulf_noconv(prod, d32r, apowers[0], dt, dn, nint, nlen, dn0);
21580Sstevel@tonic-gate 	conv_i32_to_d32(d32r, prod, nlen);
21590Sstevel@tonic-gate 	for (i = 1; i < apowerssize; i++) {
21600Sstevel@tonic-gate 		mont_mulf_noconv(prod, d32r, apowers[i - 1],
21610Sstevel@tonic-gate 		    dt, dn, nint, nlen, dn0);
21620Sstevel@tonic-gate 		conv_i32_to_d16(apowers[i], prod, nlen);
21630Sstevel@tonic-gate 	}
21640Sstevel@tonic-gate 
21656557Sfr41279 #if (BIG_CHUNK_SIZE == 32)
21666557Sfr41279 	for (i = 0; i < tmp->len; i++) {
21676557Sfr41279 		prod[i] = tmp->value[i];
21686557Sfr41279 	}
21696557Sfr41279 	for (; i < nlen + 1; i++) {
21706557Sfr41279 		prod[i] = 0;
21716557Sfr41279 	}
21726557Sfr41279 #else
21736557Sfr41279 	for (i = 0; i < tmp->len; i++) {
21746557Sfr41279 		prod[2 * i] = (uint32_t)(tmp->value[i] & 0xffffffffULL);
21756557Sfr41279 		prod[2 * i + 1] = (uint32_t)(tmp->value[i] >> 32);
21766557Sfr41279 	}
21776557Sfr41279 	for (i = tmp->len * 2; i < nlen + 1; i++) {
21786557Sfr41279 		prod[i] = 0;
21796557Sfr41279 	}
21806557Sfr41279 #endif
21816557Sfr41279 
21826557Sfr41279 	bitind = nbits % BIG_CHUNK_SIZE;
21830Sstevel@tonic-gate 	k = 0;
21840Sstevel@tonic-gate 	l = 0;
21850Sstevel@tonic-gate 	p = 0;
21860Sstevel@tonic-gate 	bitcount = 0;
21876557Sfr41279 	for (i = nbits / BIG_CHUNK_SIZE; i >= 0; i--) {
21880Sstevel@tonic-gate 		for (j = bitind - 1; j >= 0; j--) {
21890Sstevel@tonic-gate 			bit = (e->value[i] >> j) & 1;
21900Sstevel@tonic-gate 			if ((bitcount == 0) && (bit == 0)) {
21910Sstevel@tonic-gate 				conv_i32_to_d32_and_d16(d32r, d16r,
21920Sstevel@tonic-gate 				    prod, nlen);
21930Sstevel@tonic-gate 				mont_mulf_noconv(prod, d32r, d16r,
21940Sstevel@tonic-gate 				    dt, dn, nint, nlen, dn0);
21950Sstevel@tonic-gate 			} else {
21960Sstevel@tonic-gate 				bitcount++;
21970Sstevel@tonic-gate 				p = p * 2 + bit;
21980Sstevel@tonic-gate 				if (bit == 1) {
21990Sstevel@tonic-gate 					k = k + l + 1;
22000Sstevel@tonic-gate 					l = 0;
22010Sstevel@tonic-gate 				} else {
22020Sstevel@tonic-gate 					l++;
22030Sstevel@tonic-gate 				}
22040Sstevel@tonic-gate 				if (bitcount == groupbits) {
22050Sstevel@tonic-gate 					for (m = 0; m < k; m++) {
22066557Sfr41279 						conv_i32_to_d32_and_d16(d32r,
22076557Sfr41279 						    d16r, prod, nlen);
22080Sstevel@tonic-gate 						mont_mulf_noconv(prod, d32r,
22090Sstevel@tonic-gate 						    d16r, dt, dn, nint,
22100Sstevel@tonic-gate 						    nlen, dn0);
22110Sstevel@tonic-gate 					}
22120Sstevel@tonic-gate 					conv_i32_to_d32(d32r, prod, nlen);
22130Sstevel@tonic-gate 					mont_mulf_noconv(prod, d32r,
22148933Sopensolaris@drydog.com 					    apowers[p >> (l + 1)],
22150Sstevel@tonic-gate 					    dt, dn, nint, nlen, dn0);
22160Sstevel@tonic-gate 					for (m = 0; m < l; m++) {
22176557Sfr41279 						conv_i32_to_d32_and_d16(d32r,
22186557Sfr41279 						    d16r, prod, nlen);
22190Sstevel@tonic-gate 						mont_mulf_noconv(prod, d32r,
22200Sstevel@tonic-gate 						    d16r, dt, dn, nint,
22210Sstevel@tonic-gate 						    nlen, dn0);
22220Sstevel@tonic-gate 					}
22230Sstevel@tonic-gate 					k = 0;
22240Sstevel@tonic-gate 					l = 0;
22250Sstevel@tonic-gate 					p = 0;
22260Sstevel@tonic-gate 					bitcount = 0;
22270Sstevel@tonic-gate 				}
22280Sstevel@tonic-gate 			}
22290Sstevel@tonic-gate 		}
22306557Sfr41279 		bitind = BIG_CHUNK_SIZE;
22310Sstevel@tonic-gate 	}
22320Sstevel@tonic-gate 
22330Sstevel@tonic-gate 	for (m = 0; m < k; m++) {
22340Sstevel@tonic-gate 		conv_i32_to_d32_and_d16(d32r, d16r, prod, nlen);
22350Sstevel@tonic-gate 		mont_mulf_noconv(prod, d32r, d16r, dt, dn, nint, nlen, dn0);
22360Sstevel@tonic-gate 	}
22370Sstevel@tonic-gate 	if (p != 0) {
22380Sstevel@tonic-gate 		conv_i32_to_d32(d32r, prod, nlen);
22390Sstevel@tonic-gate 		mont_mulf_noconv(prod, d32r, apowers[p >> (l + 1)],
22400Sstevel@tonic-gate 		    dt, dn, nint, nlen, dn0);
22410Sstevel@tonic-gate 	}
22420Sstevel@tonic-gate 	for (m = 0; m < l; m++) {
22430Sstevel@tonic-gate 		conv_i32_to_d32_and_d16(d32r, d16r, prod, nlen);
22440Sstevel@tonic-gate 		mont_mulf_noconv(prod, d32r, d16r, dt, dn, nint, nlen, dn0);
22450Sstevel@tonic-gate 	}
22460Sstevel@tonic-gate 
22476557Sfr41279 #if (BIG_CHUNK_SIZE == 32)
22486557Sfr41279 	for (i = 0; i < nlen; i++) {
22496557Sfr41279 		result->value[i] = prod[i];
22506557Sfr41279 	}
22516557Sfr41279 	for (i = nlen - 1; (i > 0) && (prod[i] == 0); i--)
22526557Sfr41279 		;
22536557Sfr41279 #else
22546557Sfr41279 	for (i = 0; i < nlen / 2; i++) {
22556557Sfr41279 		result->value[i] = (uint64_t)(prod[2 * i]) +
22566557Sfr41279 		    (((uint64_t)(prod[2 * i + 1])) << 32);
22576557Sfr41279 	}
22586557Sfr41279 	for (i = nlen / 2 - 1; (i > 0) && (result->value[i] == 0); i--)
22596557Sfr41279 		;
22606557Sfr41279 #endif
22616557Sfr41279 	result->len = i + 1;
22626557Sfr41279 
22630Sstevel@tonic-gate ret:
22640Sstevel@tonic-gate 	for (i = apowerssize - 1; i >= 0; i--) {
22650Sstevel@tonic-gate 		if (apowers[i] != NULL)
22660Sstevel@tonic-gate 			big_free(apowers[i], (2 * nlen + 1) * sizeof (double));
22670Sstevel@tonic-gate 	}
22686557Sfr41279 	if (d32r != NULL) {
22690Sstevel@tonic-gate 		big_free(d32r, nlen * sizeof (double));
22706557Sfr41279 	}
22716557Sfr41279 	if (d16r != NULL) {
22720Sstevel@tonic-gate 		big_free(d16r, (2 * nlen + 1) * sizeof (double));
22736557Sfr41279 	}
22746557Sfr41279 	if (prod != NULL) {
22750Sstevel@tonic-gate 		big_free(prod, (nlen + 1) * sizeof (uint32_t));
22766557Sfr41279 	}
22776557Sfr41279 	if (nint != NULL) {
22780Sstevel@tonic-gate 		big_free(nint, nlen * sizeof (uint32_t));
22796557Sfr41279 	}
22806557Sfr41279 	if (dt != NULL) {
22810Sstevel@tonic-gate 		big_free(dt, (4 * nlen + 2) * sizeof (double));
22826557Sfr41279 	}
22836557Sfr41279 	if (dn != NULL) {
22840Sstevel@tonic-gate 		big_free(dn, nlen * sizeof (double));
22856557Sfr41279 	}
22860Sstevel@tonic-gate 
22870Sstevel@tonic-gate #ifdef _KERNEL
22886557Sfr41279 	big_restorefp(fpu);
22890Sstevel@tonic-gate #endif
22900Sstevel@tonic-gate 
22910Sstevel@tonic-gate 	return (err);
22920Sstevel@tonic-gate }
22930Sstevel@tonic-gate 
22940Sstevel@tonic-gate #endif /* USE_FLOATING_POINT */
22950Sstevel@tonic-gate 
22960Sstevel@tonic-gate 
22970Sstevel@tonic-gate BIG_ERR_CODE
22986557Sfr41279 big_modexp_ext(BIGNUM *result, BIGNUM *a, BIGNUM *e, BIGNUM *n, BIGNUM *n_rr,
22996557Sfr41279     big_modexp_ncp_info_t *info)
23000Sstevel@tonic-gate {
23016557Sfr41279 	BIGNUM		ma, tmp, rr;
23026557Sfr41279 	BIG_CHUNK_TYPE	mavalue[BIGTMPSIZE];
23036557Sfr41279 	BIG_CHUNK_TYPE	tmpvalue[BIGTMPSIZE];
23046557Sfr41279 	BIG_CHUNK_TYPE	rrvalue[BIGTMPSIZE];
23056557Sfr41279 	BIG_ERR_CODE	err;
23066557Sfr41279 	BIG_CHUNK_TYPE	n0;
23076557Sfr41279 
23086557Sfr41279 	if ((err = big_init1(&ma, n->len, mavalue, arraysize(mavalue)))	!=
23096557Sfr41279 	    BIG_OK) {
23100Sstevel@tonic-gate 		return (err);
23116557Sfr41279 	}
23126557Sfr41279 	ma.len = 1;
23136557Sfr41279 	ma.value[0] = 0;
23146557Sfr41279 
23156557Sfr41279 	if ((err = big_init1(&tmp, 2 * n->len + 1,
23166557Sfr41279 	    tmpvalue, arraysize(tmpvalue))) != BIG_OK) {
23170Sstevel@tonic-gate 		goto ret1;
23186557Sfr41279 	}
23196557Sfr41279 
23208933Sopensolaris@drydog.com 	/* clear the malloced bit to help cleanup */
23216557Sfr41279 	rr.malloced = 0;
23228933Sopensolaris@drydog.com 
23236557Sfr41279 	if (n_rr == NULL) {
23246557Sfr41279 		if ((err = big_init1(&rr, 2 * n->len + 1,
23256557Sfr41279 		    rrvalue, arraysize(rrvalue))) != BIG_OK) {
23266557Sfr41279 			goto ret2;
23276557Sfr41279 		}
23286557Sfr41279 		if (big_mont_rr(&rr, n) != BIG_OK) {
23296557Sfr41279 			goto ret;
23306557Sfr41279 		}
23316557Sfr41279 		n_rr = &rr;
23326557Sfr41279 	}
23336557Sfr41279 
23346557Sfr41279 	n0 = big_n0(n->value[0]);
23356557Sfr41279 
23366557Sfr41279 	if (big_cmp_abs(a, n) > 0) {
23376557Sfr41279 		if ((err = big_div_pos(NULL, &ma, a, n)) != BIG_OK) {
23386557Sfr41279 			goto ret;
23396557Sfr41279 		}
23406557Sfr41279 		err = big_mont_conv(&ma, &ma, n, n0, n_rr);
23416557Sfr41279 	} else {
23426557Sfr41279 		err = big_mont_conv(&ma, a, n, n0, n_rr);
23436557Sfr41279 	}
23446557Sfr41279 	if (err != BIG_OK) {
23456557Sfr41279 		goto ret;
23466557Sfr41279 	}
23476557Sfr41279 
23486557Sfr41279 	tmp.len = 1;
23496557Sfr41279 	tmp.value[0] = 1;
23506557Sfr41279 	if ((err = big_mont_conv(&tmp, &tmp, n, n0, n_rr)) != BIG_OK) {
23516557Sfr41279 		goto ret;
23526557Sfr41279 	}
23536557Sfr41279 
23546557Sfr41279 	if ((info != NULL) && (info->func != NULL)) {
23556557Sfr41279 		err = (*(info->func))(&tmp, &ma, e, n, &tmp, n0,
23566557Sfr41279 		    info->ncp, info->reqp);
23576557Sfr41279 	} else {
23586557Sfr41279 		err = big_modexp_ncp_sw(&tmp, &ma, e, n, &tmp, n0);
23596557Sfr41279 	}
23606557Sfr41279 	if (err != BIG_OK) {
23616557Sfr41279 		goto ret;
23626557Sfr41279 	}
23636557Sfr41279 
23646557Sfr41279 	ma.value[0] = 1;
23656557Sfr41279 	ma.len = 1;
23666557Sfr41279 	if ((err = big_mont_mul(&tmp, &tmp, &ma, n, n0)) != BIG_OK) {
23676557Sfr41279 		goto ret;
23686557Sfr41279 	}
23696557Sfr41279 	err = big_copy(result, &tmp);
23706557Sfr41279 
23716557Sfr41279 ret:
23726557Sfr41279 	if (rr.malloced) {
23736557Sfr41279 		big_finish(&rr);
23746557Sfr41279 	}
23756557Sfr41279 ret2:
23766557Sfr41279 	big_finish(&tmp);
23776557Sfr41279 ret1:
23786557Sfr41279 	big_finish(&ma);
23796557Sfr41279 
23806557Sfr41279 	return (err);
23816557Sfr41279 }
23826557Sfr41279 
23836557Sfr41279 BIG_ERR_CODE
23846557Sfr41279 big_modexp(BIGNUM *result, BIGNUM *a, BIGNUM *e, BIGNUM *n, BIGNUM *n_rr)
23856557Sfr41279 {
23866557Sfr41279 	return (big_modexp_ext(result, a, e, n, n_rr, NULL));
23876557Sfr41279 }
23886557Sfr41279 
23896557Sfr41279 
23906557Sfr41279 BIG_ERR_CODE
23916557Sfr41279 big_modexp_crt_ext(BIGNUM *result, BIGNUM *a, BIGNUM *dmodpminus1,
23926557Sfr41279     BIGNUM *dmodqminus1, BIGNUM *p, BIGNUM *q, BIGNUM *pinvmodq,
23936557Sfr41279     BIGNUM *p_rr, BIGNUM *q_rr, big_modexp_ncp_info_t *info)
23946557Sfr41279 {
23956557Sfr41279 	BIGNUM		ap, aq, tmp;
23966557Sfr41279 	int		alen, biglen, sign;
23976557Sfr41279 	BIG_ERR_CODE	err;
23986557Sfr41279 
23996557Sfr41279 	if (p->len > q->len) {
24006557Sfr41279 		biglen = p->len;
24016557Sfr41279 	} else {
24026557Sfr41279 		biglen = q->len;
24036557Sfr41279 	}
24046557Sfr41279 
24056557Sfr41279 	if ((err = big_init1(&ap, p->len, NULL, 0)) != BIG_OK) {
24066557Sfr41279 		return (err);
24076557Sfr41279 	}
24086557Sfr41279 	if ((err = big_init1(&aq, q->len, NULL, 0)) != BIG_OK) {
24096557Sfr41279 		goto ret1;
24106557Sfr41279 	}
24116557Sfr41279 	if ((err = big_init1(&tmp, biglen + q->len + 1, NULL, 0)) != BIG_OK) {
24120Sstevel@tonic-gate 		goto ret2;
24136557Sfr41279 	}
24140Sstevel@tonic-gate 
24150Sstevel@tonic-gate 	/*
24160Sstevel@tonic-gate 	 * check whether a is too short - to avoid timing attacks
24170Sstevel@tonic-gate 	 */
24180Sstevel@tonic-gate 	alen = a->len;
24190Sstevel@tonic-gate 	while ((alen > p->len) && (a->value[alen - 1] == 0)) {
24200Sstevel@tonic-gate 		alen--;
24210Sstevel@tonic-gate 	}
24220Sstevel@tonic-gate 	if (alen < p->len + q->len) {
24230Sstevel@tonic-gate 		/*
24240Sstevel@tonic-gate 		 * a is too short, add p*q to it before
24250Sstevel@tonic-gate 		 * taking it modulo p and q
24260Sstevel@tonic-gate 		 * this will also affect timing, but this difference
24270Sstevel@tonic-gate 		 * does not depend on p or q, only on a
24280Sstevel@tonic-gate 		 * (in "normal" operation, this path will never be
24290Sstevel@tonic-gate 		 * taken, so it is not a performance penalty
24300Sstevel@tonic-gate 		 */
24316557Sfr41279 		if ((err = big_mul(&tmp, p, q)) != BIG_OK) {
24320Sstevel@tonic-gate 			goto ret;
24336557Sfr41279 		}
24346557Sfr41279 		if ((err = big_add(&tmp, &tmp, a)) != BIG_OK) {
24356557Sfr41279 			goto ret;
24366557Sfr41279 		}
24376557Sfr41279 		if ((err = big_div_pos(NULL, &ap, &tmp, p)) != BIG_OK) {
24380Sstevel@tonic-gate 			goto ret;
24396557Sfr41279 		}
24406557Sfr41279 		if ((err = big_div_pos(NULL, &aq, &tmp, q)) != BIG_OK) {
24410Sstevel@tonic-gate 			goto ret;
24426557Sfr41279 		}
24436557Sfr41279 	} else {
24446557Sfr41279 		if ((err = big_div_pos(NULL, &ap, a, p)) != BIG_OK) {
24450Sstevel@tonic-gate 			goto ret;
24466557Sfr41279 		}
24476557Sfr41279 		if ((err = big_div_pos(NULL, &aq, a, q)) != BIG_OK) {
24480Sstevel@tonic-gate 			goto ret;
24496557Sfr41279 		}
24500Sstevel@tonic-gate 	}
24510Sstevel@tonic-gate 
24526557Sfr41279 	if ((err = big_modexp_ext(&ap, &ap, dmodpminus1, p, p_rr, info)) !=
24536557Sfr41279 	    BIG_OK) {
24540Sstevel@tonic-gate 		goto ret;
24556557Sfr41279 	}
24566557Sfr41279 	if ((err = big_modexp_ext(&aq, &aq, dmodqminus1, q, q_rr, info)) !=
24576557Sfr41279 	    BIG_OK) {
24580Sstevel@tonic-gate 		goto ret;
24596557Sfr41279 	}
24606557Sfr41279 	if ((err = big_sub(&tmp, &aq, &ap)) != BIG_OK) {
24610Sstevel@tonic-gate 		goto ret;
24626557Sfr41279 	}
24636557Sfr41279 	if ((err = big_mul(&tmp, &tmp, pinvmodq)) != BIG_OK) {
24640Sstevel@tonic-gate 		goto ret;
24656557Sfr41279 	}
24660Sstevel@tonic-gate 	sign = tmp.sign;
24670Sstevel@tonic-gate 	tmp.sign = 1;
24686557Sfr41279 	if ((err = big_div_pos(NULL, &aq, &tmp, q)) != BIG_OK) {
24690Sstevel@tonic-gate 		goto ret;
24706557Sfr41279 	}
24710Sstevel@tonic-gate 	if ((sign == -1) && (!big_is_zero(&aq))) {
24720Sstevel@tonic-gate 		(void) big_sub_pos(&aq, q, &aq);
24730Sstevel@tonic-gate 	}
24746557Sfr41279 	if ((err = big_mul(&tmp, &aq, p)) != BIG_OK) {
24750Sstevel@tonic-gate 		goto ret;
24766557Sfr41279 	}
24770Sstevel@tonic-gate 	err = big_add_abs(result, &ap, &tmp);
24780Sstevel@tonic-gate 
24790Sstevel@tonic-gate ret:
24800Sstevel@tonic-gate 	big_finish(&tmp);
24810Sstevel@tonic-gate ret2:
24820Sstevel@tonic-gate 	big_finish(&aq);
24830Sstevel@tonic-gate ret1:
24840Sstevel@tonic-gate 	big_finish(&ap);
24850Sstevel@tonic-gate 
24860Sstevel@tonic-gate 	return (err);
24870Sstevel@tonic-gate }
24880Sstevel@tonic-gate 
24890Sstevel@tonic-gate 
24906557Sfr41279 BIG_ERR_CODE
24916557Sfr41279 big_modexp_crt(BIGNUM *result, BIGNUM *a, BIGNUM *dmodpminus1,
24926557Sfr41279     BIGNUM *dmodqminus1, BIGNUM *p, BIGNUM *q, BIGNUM *pinvmodq,
24936557Sfr41279     BIGNUM *p_rr, BIGNUM *q_rr)
24946557Sfr41279 {
24956557Sfr41279 	return (big_modexp_crt_ext(result, a, dmodpminus1, dmodqminus1,
24966557Sfr41279 	    p, q, pinvmodq, p_rr, q_rr, NULL));
24976557Sfr41279 }
24986557Sfr41279 
24996557Sfr41279 
25006557Sfr41279 static BIG_CHUNK_TYPE onearr[1] = {(BIG_CHUNK_TYPE)1};
2501*12719SRod.Evans@Sun.COM #if	!defined(NO_BIG_ONE)
25026557Sfr41279 BIGNUM big_One = {1, 1, 1, 0, onearr};
2503*12719SRod.Evans@Sun.COM #endif
25046557Sfr41279 
25056557Sfr41279 static BIG_CHUNK_TYPE twoarr[1] = {(BIG_CHUNK_TYPE)2};
2506*12719SRod.Evans@Sun.COM #if	!defined(NO_BIG_TWO)
25076557Sfr41279 BIGNUM big_Two = {1, 1, 1, 0, twoarr};
2508*12719SRod.Evans@Sun.COM #endif
25096557Sfr41279 
25106557Sfr41279 static BIG_CHUNK_TYPE fourarr[1] = {(BIG_CHUNK_TYPE)4};
25116557Sfr41279 static BIGNUM big_Four = {1, 1, 1, 0, fourarr};
25126557Sfr41279 
25130Sstevel@tonic-gate 
25140Sstevel@tonic-gate BIG_ERR_CODE
25150Sstevel@tonic-gate big_sqrt_pos(BIGNUM *result, BIGNUM *n)
25160Sstevel@tonic-gate {
25176557Sfr41279 	BIGNUM		*high, *low, *mid, *t;
25186557Sfr41279 	BIGNUM		t1, t2, t3, prod;
25196557Sfr41279 	BIG_CHUNK_TYPE	t1value[BIGTMPSIZE];
25206557Sfr41279 	BIG_CHUNK_TYPE	t2value[BIGTMPSIZE];
25216557Sfr41279 	BIG_CHUNK_TYPE	t3value[BIGTMPSIZE];
25226557Sfr41279 	BIG_CHUNK_TYPE	prodvalue[BIGTMPSIZE];
25238933Sopensolaris@drydog.com 	int		i, diff;
25248933Sopensolaris@drydog.com 	uint32_t	nbits, nrootbits, highbits;
25256557Sfr41279 	BIG_ERR_CODE	err;
25260Sstevel@tonic-gate 
25270Sstevel@tonic-gate 	nbits = big_numbits(n);
25280Sstevel@tonic-gate 
25290Sstevel@tonic-gate 	if ((err = big_init1(&t1, n->len + 1,
25300Sstevel@tonic-gate 	    t1value, arraysize(t1value))) != BIG_OK)
25310Sstevel@tonic-gate 		return (err);
25320Sstevel@tonic-gate 	if ((err = big_init1(&t2, n->len + 1,
25330Sstevel@tonic-gate 	    t2value, arraysize(t2value))) != BIG_OK)
25340Sstevel@tonic-gate 		goto ret1;
25350Sstevel@tonic-gate 	if ((err = big_init1(&t3, n->len + 1,
25360Sstevel@tonic-gate 	    t3value, arraysize(t3value))) != BIG_OK)
25370Sstevel@tonic-gate 		goto ret2;
25380Sstevel@tonic-gate 	if ((err = big_init1(&prod, n->len + 1,
25390Sstevel@tonic-gate 	    prodvalue, arraysize(prodvalue))) != BIG_OK)
25400Sstevel@tonic-gate 		goto ret3;
25410Sstevel@tonic-gate 
25420Sstevel@tonic-gate 	nrootbits = (nbits + 1) / 2;
25436557Sfr41279 	t1.len = t2.len = t3.len = (nrootbits - 1) / BIG_CHUNK_SIZE + 1;
25440Sstevel@tonic-gate 	for (i = 0; i < t1.len; i++) {
25450Sstevel@tonic-gate 		t1.value[i] = 0;
25466557Sfr41279 		t2.value[i] = BIG_CHUNK_ALLBITS;
25470Sstevel@tonic-gate 	}
25486557Sfr41279 	highbits = nrootbits - BIG_CHUNK_SIZE * (t1.len - 1);
25496557Sfr41279 	if (highbits == BIG_CHUNK_SIZE) {
25506557Sfr41279 		t1.value[t1.len - 1] = BIG_CHUNK_HIGHBIT;
25516557Sfr41279 		t2.value[t2.len - 1] = BIG_CHUNK_ALLBITS;
25520Sstevel@tonic-gate 	} else {
25536557Sfr41279 		t1.value[t1.len - 1] = (BIG_CHUNK_TYPE)1 << (highbits - 1);
25540Sstevel@tonic-gate 		t2.value[t2.len - 1] = 2 * t1.value[t1.len - 1] - 1;
25550Sstevel@tonic-gate 	}
25566557Sfr41279 
25570Sstevel@tonic-gate 	high = &t2;
25580Sstevel@tonic-gate 	low = &t1;
25590Sstevel@tonic-gate 	mid = &t3;
25600Sstevel@tonic-gate 
25616557Sfr41279 	if ((err = big_mul(&prod, high, high)) != BIG_OK) {
25620Sstevel@tonic-gate 		goto ret;
25636557Sfr41279 	}
25640Sstevel@tonic-gate 	diff = big_cmp_abs(&prod, n);
25650Sstevel@tonic-gate 	if (diff <= 0) {
25660Sstevel@tonic-gate 		err = big_copy(result, high);
25670Sstevel@tonic-gate 		goto ret;
25680Sstevel@tonic-gate 	}
25690Sstevel@tonic-gate 
25700Sstevel@tonic-gate 	(void) big_sub_pos(mid, high, low);
25716557Sfr41279 	while (big_cmp_abs(&big_One, mid) != 0) {
25720Sstevel@tonic-gate 		(void) big_add_abs(mid, high, low);
25730Sstevel@tonic-gate 		(void) big_half_pos(mid, mid);
25740Sstevel@tonic-gate 		if ((err = big_mul(&prod, mid, mid)) != BIG_OK)
25750Sstevel@tonic-gate 			goto ret;
25760Sstevel@tonic-gate 		diff = big_cmp_abs(&prod, n);
25770Sstevel@tonic-gate 		if (diff > 0) {
25780Sstevel@tonic-gate 			t = high;
25790Sstevel@tonic-gate 			high = mid;
25800Sstevel@tonic-gate 			mid = t;
25810Sstevel@tonic-gate 		} else if (diff < 0) {
25820Sstevel@tonic-gate 			t = low;
25830Sstevel@tonic-gate 			low = mid;
25840Sstevel@tonic-gate 			mid = t;
25850Sstevel@tonic-gate 		} else {
25860Sstevel@tonic-gate 			err = big_copy(result, low);
25870Sstevel@tonic-gate 			goto ret;
25880Sstevel@tonic-gate 		}
25890Sstevel@tonic-gate 		(void) big_sub_pos(mid, high, low);
25900Sstevel@tonic-gate 	}
25910Sstevel@tonic-gate 
25920Sstevel@tonic-gate 	err = big_copy(result, low);
25930Sstevel@tonic-gate ret:
25940Sstevel@tonic-gate 	if (prod.malloced) big_finish(&prod);
25950Sstevel@tonic-gate ret3:
25960Sstevel@tonic-gate 	if (t3.malloced) big_finish(&t3);
25970Sstevel@tonic-gate ret2:
25980Sstevel@tonic-gate 	if (t2.malloced) big_finish(&t2);
25990Sstevel@tonic-gate ret1:
26000Sstevel@tonic-gate 	if (t1.malloced) big_finish(&t1);
26010Sstevel@tonic-gate 
26020Sstevel@tonic-gate 	return (err);
26030Sstevel@tonic-gate }
26040Sstevel@tonic-gate 
26050Sstevel@tonic-gate 
26060Sstevel@tonic-gate BIG_ERR_CODE
26070Sstevel@tonic-gate big_Jacobi_pos(int *jac, BIGNUM *nn, BIGNUM *mm)
26080Sstevel@tonic-gate {
26096557Sfr41279 	BIGNUM		*t, *tmp2, *m, *n;
26106557Sfr41279 	BIGNUM		t1, t2, t3;
26116557Sfr41279 	BIG_CHUNK_TYPE	t1value[BIGTMPSIZE];
26126557Sfr41279 	BIG_CHUNK_TYPE	t2value[BIGTMPSIZE];
26136557Sfr41279 	BIG_CHUNK_TYPE	t3value[BIGTMPSIZE];
26146557Sfr41279 	int		len, err;
26150Sstevel@tonic-gate 
26160Sstevel@tonic-gate 	if (big_is_zero(nn) ||
26170Sstevel@tonic-gate 	    (((nn->value[0] & 1) | (mm->value[0] & 1)) == 0)) {
26180Sstevel@tonic-gate 		*jac = 0;
26190Sstevel@tonic-gate 		return (BIG_OK);
26200Sstevel@tonic-gate 	}
26210Sstevel@tonic-gate 
26226557Sfr41279 	if (nn->len > mm->len) {
26236557Sfr41279 		len = nn->len;
26246557Sfr41279 	} else {
26256557Sfr41279 		len = mm->len;
26266557Sfr41279 	}
26270Sstevel@tonic-gate 
26280Sstevel@tonic-gate 	if ((err = big_init1(&t1, len,
26296557Sfr41279 	    t1value, arraysize(t1value))) != BIG_OK) {
26300Sstevel@tonic-gate 		return (err);
26316557Sfr41279 	}
26320Sstevel@tonic-gate 	if ((err = big_init1(&t2, len,
26336557Sfr41279 	    t2value, arraysize(t2value))) != BIG_OK) {
26340Sstevel@tonic-gate 		goto ret1;
26356557Sfr41279 	}
26360Sstevel@tonic-gate 	if ((err = big_init1(&t3, len,
26376557Sfr41279 	    t3value, arraysize(t3value))) != BIG_OK) {
26380Sstevel@tonic-gate 		goto ret2;
26396557Sfr41279 	}
26400Sstevel@tonic-gate 
26410Sstevel@tonic-gate 	n = &t1;
26420Sstevel@tonic-gate 	m = &t2;
26430Sstevel@tonic-gate 	tmp2 = &t3;
26440Sstevel@tonic-gate 
26450Sstevel@tonic-gate 	(void) big_copy(n, nn);
26460Sstevel@tonic-gate 	(void) big_copy(m, mm);
26470Sstevel@tonic-gate 
26480Sstevel@tonic-gate 	*jac = 1;
26496557Sfr41279 	while (big_cmp_abs(&big_One, m) != 0) {
26500Sstevel@tonic-gate 		if (big_is_zero(n)) {
26510Sstevel@tonic-gate 			*jac = 0;
26520Sstevel@tonic-gate 			goto ret;
26530Sstevel@tonic-gate 		}
26540Sstevel@tonic-gate 		if ((m->value[0] & 1) == 0) {
26550Sstevel@tonic-gate 			if (((n->value[0] & 7) == 3) ||
26566557Sfr41279 			    ((n->value[0] & 7) == 5))
26576557Sfr41279 				*jac = -*jac;
26580Sstevel@tonic-gate 			(void) big_half_pos(m, m);
26590Sstevel@tonic-gate 		} else if ((n->value[0] & 1) == 0) {
26600Sstevel@tonic-gate 			if (((m->value[0] & 7) == 3) ||
26616557Sfr41279 			    ((m->value[0] & 7) == 5))
26626557Sfr41279 				*jac = -*jac;
26630Sstevel@tonic-gate 			(void) big_half_pos(n, n);
26640Sstevel@tonic-gate 		} else {
26650Sstevel@tonic-gate 			if (((m->value[0] & 3) == 3) &&
26660Sstevel@tonic-gate 			    ((n->value[0] & 3) == 3)) {
26670Sstevel@tonic-gate 				*jac = -*jac;
26680Sstevel@tonic-gate 			}
26696557Sfr41279 			if ((err = big_div_pos(NULL, tmp2, m, n)) != BIG_OK) {
26700Sstevel@tonic-gate 				goto ret;
26716557Sfr41279 			}
26720Sstevel@tonic-gate 			t = tmp2;
26730Sstevel@tonic-gate 			tmp2 = m;
26740Sstevel@tonic-gate 			m = n;
26750Sstevel@tonic-gate 			n = t;
26760Sstevel@tonic-gate 		}
26770Sstevel@tonic-gate 	}
26780Sstevel@tonic-gate 	err = BIG_OK;
26790Sstevel@tonic-gate 
26800Sstevel@tonic-gate ret:
26810Sstevel@tonic-gate 	if (t3.malloced) big_finish(&t3);
26820Sstevel@tonic-gate ret2:
26830Sstevel@tonic-gate 	if (t2.malloced) big_finish(&t2);
26840Sstevel@tonic-gate ret1:
26850Sstevel@tonic-gate 	if (t1.malloced) big_finish(&t1);
26860Sstevel@tonic-gate 
26870Sstevel@tonic-gate 	return (err);
26880Sstevel@tonic-gate }
26890Sstevel@tonic-gate 
26900Sstevel@tonic-gate 
26910Sstevel@tonic-gate BIG_ERR_CODE
26920Sstevel@tonic-gate big_Lucas(BIGNUM *Lkminus1, BIGNUM *Lk, BIGNUM *p, BIGNUM *k, BIGNUM *n)
26930Sstevel@tonic-gate {
26948933Sopensolaris@drydog.com 	int		i;
26958933Sopensolaris@drydog.com 	uint32_t	m, w;
26966557Sfr41279 	BIG_CHUNK_TYPE	bit;
26976557Sfr41279 	BIGNUM		ki, tmp, tmp2;
26986557Sfr41279 	BIG_CHUNK_TYPE	kivalue[BIGTMPSIZE];
26996557Sfr41279 	BIG_CHUNK_TYPE	tmpvalue[BIGTMPSIZE];
27006557Sfr41279 	BIG_CHUNK_TYPE	tmp2value[BIGTMPSIZE];
27016557Sfr41279 	BIG_ERR_CODE	err;
27026557Sfr41279 
27036557Sfr41279 	if (big_cmp_abs(k, &big_One) == 0) {
27040Sstevel@tonic-gate 		(void) big_copy(Lk, p);
27056557Sfr41279 		(void) big_copy(Lkminus1, &big_Two);
27060Sstevel@tonic-gate 		return (BIG_OK);
27070Sstevel@tonic-gate 	}
27080Sstevel@tonic-gate 
27090Sstevel@tonic-gate 	if ((err = big_init1(&ki, k->len + 1,
27100Sstevel@tonic-gate 	    kivalue, arraysize(kivalue))) != BIG_OK)
27110Sstevel@tonic-gate 		return (err);
27120Sstevel@tonic-gate 
27138933Sopensolaris@drydog.com 	if ((err = big_init1(&tmp, 2 * n->len + 1,
27140Sstevel@tonic-gate 	    tmpvalue, arraysize(tmpvalue))) != BIG_OK)
27150Sstevel@tonic-gate 		goto ret1;
27160Sstevel@tonic-gate 
27170Sstevel@tonic-gate 	if ((err = big_init1(&tmp2, n->len,
27180Sstevel@tonic-gate 	    tmp2value, arraysize(tmp2value))) != BIG_OK)
27190Sstevel@tonic-gate 		goto ret2;
27200Sstevel@tonic-gate 
27210Sstevel@tonic-gate 	m = big_numbits(k);
27226557Sfr41279 	ki.len = (m - 1) / BIG_CHUNK_SIZE + 1;
27236557Sfr41279 	w = (m - 1) / BIG_CHUNK_SIZE;
27246557Sfr41279 	bit = (BIG_CHUNK_TYPE)1 << ((m - 1) % BIG_CHUNK_SIZE);
27256557Sfr41279 	for (i = 0; i < ki.len; i++) {
27266557Sfr41279 		ki.value[i] = 0;
27276557Sfr41279 	}
27280Sstevel@tonic-gate 	ki.value[ki.len - 1] = bit;
27296557Sfr41279 	if (big_cmp_abs(k, &ki) != 0) {
27300Sstevel@tonic-gate 		(void) big_double(&ki, &ki);
27316557Sfr41279 	}
27320Sstevel@tonic-gate 	(void) big_sub_pos(&ki, &ki, k);
27330Sstevel@tonic-gate 
27340Sstevel@tonic-gate 	(void) big_copy(Lk, p);
27356557Sfr41279 	(void) big_copy(Lkminus1, &big_Two);
27360Sstevel@tonic-gate 
27370Sstevel@tonic-gate 	for (i = 0; i < m; i++) {
27386557Sfr41279 		if ((err = big_mul(&tmp, Lk, Lkminus1)) != BIG_OK) {
27390Sstevel@tonic-gate 			goto ret;
27406557Sfr41279 		}
27410Sstevel@tonic-gate 		(void) big_add_abs(&tmp, &tmp, n);
27420Sstevel@tonic-gate 		(void) big_sub_pos(&tmp, &tmp, p);
27436557Sfr41279 		if ((err = big_div_pos(NULL, &tmp2, &tmp, n)) != BIG_OK) {
27440Sstevel@tonic-gate 			goto ret;
27456557Sfr41279 		}
27460Sstevel@tonic-gate 		if ((ki.value[w] & bit) != 0) {
27470Sstevel@tonic-gate 			if ((err = big_mul(&tmp, Lkminus1, Lkminus1)) !=
27486557Sfr41279 			    BIG_OK) {
27490Sstevel@tonic-gate 				goto ret;
27506557Sfr41279 			}
27510Sstevel@tonic-gate 			(void) big_add_abs(&tmp, &tmp, n);
27526557Sfr41279 			(void) big_sub_pos(&tmp, &tmp, &big_Two);
27530Sstevel@tonic-gate 			if ((err = big_div_pos(NULL, Lkminus1, &tmp, n)) !=
27546557Sfr41279 			    BIG_OK) {
27550Sstevel@tonic-gate 				goto ret;
27566557Sfr41279 			}
27570Sstevel@tonic-gate 			(void) big_copy(Lk, &tmp2);
27580Sstevel@tonic-gate 		} else {
27596557Sfr41279 			if ((err = big_mul(&tmp, Lk, Lk)) != BIG_OK) {
27600Sstevel@tonic-gate 				goto ret;
27616557Sfr41279 			}
27620Sstevel@tonic-gate 			(void) big_add_abs(&tmp, &tmp, n);
27636557Sfr41279 			(void) big_sub_pos(&tmp, &tmp, &big_Two);
27646557Sfr41279 			if ((err = big_div_pos(NULL, Lk, &tmp, n)) != BIG_OK) {
27650Sstevel@tonic-gate 				goto ret;
27666557Sfr41279 			}
27670Sstevel@tonic-gate 			(void) big_copy(Lkminus1, &tmp2);
27680Sstevel@tonic-gate 		}
27690Sstevel@tonic-gate 		bit = bit >> 1;
27700Sstevel@tonic-gate 		if (bit == 0) {
27716557Sfr41279 			bit = BIG_CHUNK_HIGHBIT;
27720Sstevel@tonic-gate 			w--;
27730Sstevel@tonic-gate 		}
27740Sstevel@tonic-gate 	}
27750Sstevel@tonic-gate 
27760Sstevel@tonic-gate 	err = BIG_OK;
27770Sstevel@tonic-gate 
27780Sstevel@tonic-gate ret:
27790Sstevel@tonic-gate 	if (tmp2.malloced) big_finish(&tmp2);
27800Sstevel@tonic-gate ret2:
27810Sstevel@tonic-gate 	if (tmp.malloced) big_finish(&tmp);
27820Sstevel@tonic-gate ret1:
27830Sstevel@tonic-gate 	if (ki.malloced) big_finish(&ki);
27840Sstevel@tonic-gate 
27850Sstevel@tonic-gate 	return (err);
27860Sstevel@tonic-gate }
27870Sstevel@tonic-gate 
27880Sstevel@tonic-gate 
27890Sstevel@tonic-gate BIG_ERR_CODE
27906557Sfr41279 big_isprime_pos_ext(BIGNUM *n, big_modexp_ncp_info_t *info)
27910Sstevel@tonic-gate {
27926557Sfr41279 	BIGNUM		o, nminus1, tmp, Lkminus1, Lk;
27936557Sfr41279 	BIG_CHUNK_TYPE	ovalue[BIGTMPSIZE];
27946557Sfr41279 	BIG_CHUNK_TYPE	nminus1value[BIGTMPSIZE];
27956557Sfr41279 	BIG_CHUNK_TYPE	tmpvalue[BIGTMPSIZE];
27966557Sfr41279 	BIG_CHUNK_TYPE	Lkminus1value[BIGTMPSIZE];
27976557Sfr41279 	BIG_CHUNK_TYPE	Lkvalue[BIGTMPSIZE];
27986557Sfr41279 	BIG_ERR_CODE	err;
27996557Sfr41279 	int		e, i, jac;
28006557Sfr41279 
28016557Sfr41279 	if (big_cmp_abs(n, &big_One) == 0) {
28020Sstevel@tonic-gate 		return (BIG_FALSE);
28036557Sfr41279 	}
28046557Sfr41279 	if (big_cmp_abs(n, &big_Two) == 0) {
28050Sstevel@tonic-gate 		return (BIG_TRUE);
28066557Sfr41279 	}
28076557Sfr41279 	if ((n->value[0] & 1) == 0) {
28080Sstevel@tonic-gate 		return (BIG_FALSE);
28096557Sfr41279 	}
28106557Sfr41279 
28116557Sfr41279 	if ((err = big_init1(&o, n->len, ovalue, arraysize(ovalue))) !=
28126557Sfr41279 	    BIG_OK) {
28130Sstevel@tonic-gate 		return (err);
28146557Sfr41279 	}
28150Sstevel@tonic-gate 
28160Sstevel@tonic-gate 	if ((err = big_init1(&nminus1, n->len,
28176557Sfr41279 	    nminus1value, arraysize(nminus1value))) != BIG_OK) {
28180Sstevel@tonic-gate 		goto ret1;
28196557Sfr41279 	}
28200Sstevel@tonic-gate 
28210Sstevel@tonic-gate 	if ((err = big_init1(&tmp, 2 * n->len,
28226557Sfr41279 	    tmpvalue, arraysize(tmpvalue))) != BIG_OK) {
28230Sstevel@tonic-gate 		goto ret2;
28246557Sfr41279 	}
28250Sstevel@tonic-gate 
28260Sstevel@tonic-gate 	if ((err = big_init1(&Lkminus1, n->len,
28276557Sfr41279 	    Lkminus1value, arraysize(Lkminus1value))) != BIG_OK) {
28280Sstevel@tonic-gate 		goto ret3;
28296557Sfr41279 	}
28300Sstevel@tonic-gate 
28310Sstevel@tonic-gate 	if ((err = big_init1(&Lk, n->len,
28326557Sfr41279 	    Lkvalue, arraysize(Lkvalue))) != BIG_OK) {
28330Sstevel@tonic-gate 		goto ret4;
28346557Sfr41279 	}
28356557Sfr41279 
28368933Sopensolaris@drydog.com 	(void) big_sub_pos(&o, n, &big_One);	/* cannot fail */
28370Sstevel@tonic-gate 	(void) big_copy(&nminus1, &o);		/* cannot fail */
28380Sstevel@tonic-gate 	e = 0;
28390Sstevel@tonic-gate 	while ((o.value[0] & 1) == 0) {
28400Sstevel@tonic-gate 		e++;
28410Sstevel@tonic-gate 		(void) big_half_pos(&o, &o);  /* cannot fail */
28420Sstevel@tonic-gate 	}
28436557Sfr41279 	if ((err = big_modexp_ext(&tmp, &big_Two, &o, n, NULL, info)) !=
28446557Sfr41279 	    BIG_OK) {
28450Sstevel@tonic-gate 		goto ret;
28466557Sfr41279 	}
28476557Sfr41279 
28480Sstevel@tonic-gate 	i = 0;
28490Sstevel@tonic-gate 	while ((i < e) &&
28506557Sfr41279 	    (big_cmp_abs(&tmp, &big_One) != 0) &&
28510Sstevel@tonic-gate 	    (big_cmp_abs(&tmp, &nminus1) != 0)) {
28526557Sfr41279 		if ((err =
28536557Sfr41279 		    big_modexp_ext(&tmp, &tmp, &big_Two, n, NULL, info)) !=
28546557Sfr41279 		    BIG_OK)
28550Sstevel@tonic-gate 			goto ret;
28560Sstevel@tonic-gate 		i++;
28570Sstevel@tonic-gate 	}
28586557Sfr41279 
28590Sstevel@tonic-gate 	if (!((big_cmp_abs(&tmp, &nminus1) == 0) ||
28606557Sfr41279 	    ((i == 0) && (big_cmp_abs(&tmp, &big_One) == 0)))) {
28610Sstevel@tonic-gate 		err = BIG_FALSE;
28620Sstevel@tonic-gate 		goto ret;
28630Sstevel@tonic-gate 	}
28640Sstevel@tonic-gate 
28656557Sfr41279 	if ((err = big_sqrt_pos(&tmp, n)) != BIG_OK) {
28660Sstevel@tonic-gate 		goto ret;
28676557Sfr41279 	}
28686557Sfr41279 
28696557Sfr41279 	if ((err = big_mul(&tmp, &tmp, &tmp)) != BIG_OK) {
28700Sstevel@tonic-gate 		goto ret;
28716557Sfr41279 	}
28720Sstevel@tonic-gate 	if (big_cmp_abs(&tmp, n) == 0) {
28730Sstevel@tonic-gate 		err = BIG_FALSE;
28740Sstevel@tonic-gate 		goto ret;
28750Sstevel@tonic-gate 	}
28760Sstevel@tonic-gate 
28776557Sfr41279 	(void) big_copy(&o, &big_Two);
28780Sstevel@tonic-gate 	do {
28796557Sfr41279 		(void) big_add_abs(&o, &o, &big_One);
28806557Sfr41279 		if ((err = big_mul(&tmp, &o, &o)) != BIG_OK) {
28810Sstevel@tonic-gate 			goto ret;
28826557Sfr41279 		}
28836557Sfr41279 		(void) big_sub_pos(&tmp, &tmp, &big_Four);
28846557Sfr41279 		if ((err = big_Jacobi_pos(&jac, &tmp, n)) != BIG_OK) {
28850Sstevel@tonic-gate 			goto ret;
28866557Sfr41279 		}
28870Sstevel@tonic-gate 	} while (jac != -1);
28880Sstevel@tonic-gate 
28896557Sfr41279 	(void) big_add_abs(&tmp, n, &big_One);
28906557Sfr41279 	if ((err = big_Lucas(&Lkminus1, &Lk, &o, &tmp, n)) != BIG_OK) {
28910Sstevel@tonic-gate 		goto ret;
28926557Sfr41279 	}
28936557Sfr41279 
28946557Sfr41279 	if ((big_cmp_abs(&Lkminus1, &o) == 0) &&
28956557Sfr41279 	    (big_cmp_abs(&Lk, &big_Two) == 0)) {
28960Sstevel@tonic-gate 		err = BIG_TRUE;
28976557Sfr41279 	} else {
28986557Sfr41279 		err = BIG_FALSE;
28996557Sfr41279 	}
29000Sstevel@tonic-gate 
29010Sstevel@tonic-gate ret:
29020Sstevel@tonic-gate 	if (Lk.malloced) big_finish(&Lk);
29030Sstevel@tonic-gate ret4:
29040Sstevel@tonic-gate 	if (Lkminus1.malloced) big_finish(&Lkminus1);
29050Sstevel@tonic-gate ret3:
29060Sstevel@tonic-gate 	if (tmp.malloced) big_finish(&tmp);
29070Sstevel@tonic-gate ret2:
29080Sstevel@tonic-gate 	if (nminus1.malloced) big_finish(&nminus1);
29090Sstevel@tonic-gate ret1:
29100Sstevel@tonic-gate 	if (o.malloced) big_finish(&o);
29110Sstevel@tonic-gate 
29120Sstevel@tonic-gate 	return (err);
29130Sstevel@tonic-gate }
29140Sstevel@tonic-gate 
29150Sstevel@tonic-gate 
29166557Sfr41279 BIG_ERR_CODE
29176557Sfr41279 big_isprime_pos(BIGNUM *n)
29186557Sfr41279 {
29196557Sfr41279 	return (big_isprime_pos_ext(n, NULL));
29206557Sfr41279 }
29216557Sfr41279 
29226557Sfr41279 
29230Sstevel@tonic-gate #define	SIEVESIZE 1000
29240Sstevel@tonic-gate 
29250Sstevel@tonic-gate 
29260Sstevel@tonic-gate BIG_ERR_CODE
29276557Sfr41279 big_nextprime_pos_ext(BIGNUM *result, BIGNUM *n, big_modexp_ncp_info_t *info)
29280Sstevel@tonic-gate {
29298933Sopensolaris@drydog.com 	static const uint32_t smallprimes[] = {
29308933Sopensolaris@drydog.com 	    3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,
29318933Sopensolaris@drydog.com 	    51, 53, 59, 61, 67, 71, 73, 79, 83, 89, 91, 97 };
29326557Sfr41279 	BIG_ERR_CODE	err;
29336557Sfr41279 	int		sieve[SIEVESIZE];
29346557Sfr41279 	int		i;
29356557Sfr41279 	uint32_t	off, p;
29366557Sfr41279 
29376557Sfr41279 	if ((err = big_copy(result, n)) != BIG_OK) {
29380Sstevel@tonic-gate 		return (err);
29396557Sfr41279 	}
29400Sstevel@tonic-gate 	result->value[0] |= 1;
29411027Srobinson 	/* CONSTCOND */
29420Sstevel@tonic-gate 	while (1) {
29430Sstevel@tonic-gate 		for (i = 0; i < SIEVESIZE; i++) sieve[i] = 0;
29440Sstevel@tonic-gate 		for (i = 0;
29456557Sfr41279 		    i < sizeof (smallprimes) / sizeof (smallprimes[0]); i++) {
29460Sstevel@tonic-gate 			p = smallprimes[i];
29476557Sfr41279 			off = big_modhalf_pos(result, p);
29480Sstevel@tonic-gate 			off = p - off;
29496557Sfr41279 			if ((off % 2) == 1) {
29506557Sfr41279 				off = off + p;
29516557Sfr41279 			}
29526557Sfr41279 			off = off / 2;
29530Sstevel@tonic-gate 			while (off < SIEVESIZE) {
29540Sstevel@tonic-gate 				sieve[off] = 1;
29550Sstevel@tonic-gate 				off = off + p;
29560Sstevel@tonic-gate 			}
29570Sstevel@tonic-gate 		}
29580Sstevel@tonic-gate 
29590Sstevel@tonic-gate 		for (i = 0; i < SIEVESIZE; i++) {
29600Sstevel@tonic-gate 			if (sieve[i] == 0) {
29616557Sfr41279 				err = big_isprime_pos_ext(result, info);
29620Sstevel@tonic-gate 				if (err != BIG_FALSE) {
29636557Sfr41279 					if (err != BIG_TRUE) {
29640Sstevel@tonic-gate 						return (err);
29656557Sfr41279 					} else {
29666557Sfr41279 						goto out;
29676557Sfr41279 					}
29680Sstevel@tonic-gate 				}
29690Sstevel@tonic-gate 
29700Sstevel@tonic-gate 			}
29716557Sfr41279 			if ((err = big_add_abs(result, result, &big_Two)) !=
29726557Sfr41279 			    BIG_OK) {
29730Sstevel@tonic-gate 				return (err);
29746557Sfr41279 			}
29750Sstevel@tonic-gate 		}
29760Sstevel@tonic-gate 	}
29776557Sfr41279 
29786557Sfr41279 out:
29796557Sfr41279 	return (BIG_OK);
29806557Sfr41279 }
29816557Sfr41279 
29826557Sfr41279 
29836557Sfr41279 BIG_ERR_CODE
29846557Sfr41279 big_nextprime_pos(BIGNUM *result, BIGNUM *n)
29856557Sfr41279 {
29866557Sfr41279 	return (big_nextprime_pos_ext(result, n, NULL));
29870Sstevel@tonic-gate }
29880Sstevel@tonic-gate 
29890Sstevel@tonic-gate 
29900Sstevel@tonic-gate BIG_ERR_CODE
29910Sstevel@tonic-gate big_nextprime_pos_slow(BIGNUM *result, BIGNUM *n)
29920Sstevel@tonic-gate {
29930Sstevel@tonic-gate 	BIG_ERR_CODE err;
29940Sstevel@tonic-gate 
29950Sstevel@tonic-gate 
29960Sstevel@tonic-gate 	if ((err = big_copy(result, n)) != BIG_OK)
29970Sstevel@tonic-gate 		return (err);
29980Sstevel@tonic-gate 	result->value[0] |= 1;
29996557Sfr41279 	while ((err = big_isprime_pos_ext(result, NULL)) != BIG_TRUE) {
30000Sstevel@tonic-gate 		if (err != BIG_FALSE)
30010Sstevel@tonic-gate 			return (err);
30026557Sfr41279 		if ((err = big_add_abs(result, result, &big_Two)) != BIG_OK)
30030Sstevel@tonic-gate 			return (err);
30040Sstevel@tonic-gate 	}
30050Sstevel@tonic-gate 	return (BIG_OK);
30060Sstevel@tonic-gate }
30070Sstevel@tonic-gate 
30080Sstevel@tonic-gate 
30090Sstevel@tonic-gate /*
30100Sstevel@tonic-gate  * given m and e, computes the rest in the equation
30110Sstevel@tonic-gate  * gcd(m, e) = cm * m + ce * e
30120Sstevel@tonic-gate  */
30130Sstevel@tonic-gate BIG_ERR_CODE
30140Sstevel@tonic-gate big_ext_gcd_pos(BIGNUM *gcd, BIGNUM *cm, BIGNUM *ce, BIGNUM *m, BIGNUM *e)
30150Sstevel@tonic-gate {
30166557Sfr41279 	BIGNUM		*xi, *ri, *riminus1, *riminus2, *t;
30176557Sfr41279 	BIGNUM		*vmi, *vei, *vmiminus1, *veiminus1;
30186557Sfr41279 	BIGNUM		t1, t2, t3, t4, t5, t6, t7, t8, tmp;
30196557Sfr41279 	BIG_CHUNK_TYPE	t1value[BIGTMPSIZE];
30206557Sfr41279 	BIG_CHUNK_TYPE	t2value[BIGTMPSIZE];
30216557Sfr41279 	BIG_CHUNK_TYPE	t3value[BIGTMPSIZE];
30226557Sfr41279 	BIG_CHUNK_TYPE	t4value[BIGTMPSIZE];
30236557Sfr41279 	BIG_CHUNK_TYPE	t5value[BIGTMPSIZE];
30246557Sfr41279 	BIG_CHUNK_TYPE	t6value[BIGTMPSIZE];
30256557Sfr41279 	BIG_CHUNK_TYPE	t7value[BIGTMPSIZE];
30266557Sfr41279 	BIG_CHUNK_TYPE	t8value[BIGTMPSIZE];
30276557Sfr41279 	BIG_CHUNK_TYPE	tmpvalue[BIGTMPSIZE];
30286557Sfr41279 	BIG_ERR_CODE	err;
30296557Sfr41279 	int		len;
30306557Sfr41279 
30316557Sfr41279 	if (big_cmp_abs(m, e) >= 0) {
30326557Sfr41279 		len = m->len;
30336557Sfr41279 	} else {
30346557Sfr41279 		len = e->len;
30356557Sfr41279 	}
30360Sstevel@tonic-gate 
30370Sstevel@tonic-gate 	if ((err = big_init1(&t1, len,
30386557Sfr41279 	    t1value, arraysize(t1value))) != BIG_OK) {
30390Sstevel@tonic-gate 		return (err);
30406557Sfr41279 	}
30410Sstevel@tonic-gate 	if ((err = big_init1(&t2, len,
30426557Sfr41279 	    t2value, arraysize(t2value))) != BIG_OK) {
30436557Sfr41279 		goto ret1;
30446557Sfr41279 	}
30450Sstevel@tonic-gate 	if ((err = big_init1(&t3, len,
30466557Sfr41279 	    t3value, arraysize(t3value))) != BIG_OK) {
30476557Sfr41279 		goto ret2;
30486557Sfr41279 	}
30490Sstevel@tonic-gate 	if ((err = big_init1(&t4, len,
30506557Sfr41279 	    t4value, arraysize(t3value))) != BIG_OK) {
30516557Sfr41279 		goto ret3;
30526557Sfr41279 	}
30530Sstevel@tonic-gate 	if ((err = big_init1(&t5, len,
30546557Sfr41279 	    t5value, arraysize(t5value))) != BIG_OK) {
30556557Sfr41279 		goto ret4;
30566557Sfr41279 	}
30570Sstevel@tonic-gate 	if ((err = big_init1(&t6, len,
30586557Sfr41279 	    t6value, arraysize(t6value))) != BIG_OK) {
30596557Sfr41279 		goto ret5;
30606557Sfr41279 	}
30610Sstevel@tonic-gate 	if ((err = big_init1(&t7, len,
30626557Sfr41279 	    t7value, arraysize(t7value))) != BIG_OK) {
30636557Sfr41279 		goto ret6;
30646557Sfr41279 	}
30650Sstevel@tonic-gate 	if ((err = big_init1(&t8, len,
30666557Sfr41279 	    t8value, arraysize(t8value))) != BIG_OK) {
30676557Sfr41279 		goto ret7;
30686557Sfr41279 	}
30690Sstevel@tonic-gate 
30700Sstevel@tonic-gate 	if ((err = big_init1(&tmp, 2 * len,
30716557Sfr41279 	    tmpvalue, arraysize(tmpvalue))) != BIG_OK) {
30720Sstevel@tonic-gate 		goto ret8;
30736557Sfr41279 	}
30740Sstevel@tonic-gate 
30750Sstevel@tonic-gate 	ri = &t1;
30760Sstevel@tonic-gate 	ri->value[0] = 1;
30770Sstevel@tonic-gate 	ri->len = 1;
30780Sstevel@tonic-gate 	xi = &t2;
30790Sstevel@tonic-gate 	riminus1 = &t3;
30800Sstevel@tonic-gate 	riminus2 = &t4;
30810Sstevel@tonic-gate 	vmi = &t5;
30820Sstevel@tonic-gate 	vei = &t6;
30830Sstevel@tonic-gate 	vmiminus1 = &t7;
30840Sstevel@tonic-gate 	veiminus1 = &t8;
30850Sstevel@tonic-gate 
30866557Sfr41279 	(void) big_copy(vmiminus1, &big_One);
30876557Sfr41279 	(void) big_copy(vmi, &big_One);
30886557Sfr41279 	(void) big_copy(veiminus1, &big_One);
30896557Sfr41279 	(void) big_copy(xi, &big_One);
30900Sstevel@tonic-gate 	vei->len = 1;
30910Sstevel@tonic-gate 	vei->value[0] = 0;
30920Sstevel@tonic-gate 
30930Sstevel@tonic-gate 	(void) big_copy(riminus1, m);
30940Sstevel@tonic-gate 	(void) big_copy(ri, e);
30950Sstevel@tonic-gate 
30960Sstevel@tonic-gate 	while (!big_is_zero(ri)) {
30970Sstevel@tonic-gate 		t = riminus2;
30980Sstevel@tonic-gate 		riminus2 = riminus1;
30990Sstevel@tonic-gate 		riminus1 = ri;
31000Sstevel@tonic-gate 		ri = t;
31016557Sfr41279 		if ((err = big_mul(&tmp, vmi, xi)) != BIG_OK) {
31020Sstevel@tonic-gate 			goto ret;
31036557Sfr41279 		}
31046557Sfr41279 		if ((err = big_sub(vmiminus1, vmiminus1, &tmp)) != BIG_OK) {
31050Sstevel@tonic-gate 			goto ret;
31066557Sfr41279 		}
31070Sstevel@tonic-gate 		t = vmiminus1;
31080Sstevel@tonic-gate 		vmiminus1 = vmi;
31090Sstevel@tonic-gate 		vmi = t;
31106557Sfr41279 		if ((err = big_mul(&tmp, vei, xi)) != BIG_OK) {
31110Sstevel@tonic-gate 			goto ret;
31126557Sfr41279 		}
31136557Sfr41279 		if ((err = big_sub(veiminus1, veiminus1, &tmp)) != BIG_OK) {
31140Sstevel@tonic-gate 			goto ret;
31156557Sfr41279 		}
31160Sstevel@tonic-gate 		t = veiminus1;
31170Sstevel@tonic-gate 		veiminus1 = vei;
31180Sstevel@tonic-gate 		vei = t;
31196557Sfr41279 		if ((err = big_div_pos(xi, ri, riminus2, riminus1)) !=
31206557Sfr41279 		    BIG_OK) {
31210Sstevel@tonic-gate 			goto ret;
31226557Sfr41279 		}
31230Sstevel@tonic-gate 	}
31246557Sfr41279 	if ((gcd != NULL) && ((err = big_copy(gcd, riminus1)) != BIG_OK)) {
31250Sstevel@tonic-gate 		goto ret;
31266557Sfr41279 	}
31276557Sfr41279 	if ((cm != NULL) && ((err = big_copy(cm, vmi)) != BIG_OK)) {
31280Sstevel@tonic-gate 		goto ret;
31296557Sfr41279 	}
31306557Sfr41279 	if (ce != NULL) {
31310Sstevel@tonic-gate 		err = big_copy(ce, vei);
31326557Sfr41279 	}
31330Sstevel@tonic-gate ret:
31340Sstevel@tonic-gate 	if (tmp.malloced) big_finish(&tmp);
31350Sstevel@tonic-gate ret8:
31360Sstevel@tonic-gate 	if (t8.malloced) big_finish(&t8);
31370Sstevel@tonic-gate ret7:
31380Sstevel@tonic-gate 	if (t7.malloced) big_finish(&t7);
31390Sstevel@tonic-gate ret6:
31400Sstevel@tonic-gate 	if (t6.malloced) big_finish(&t6);
31410Sstevel@tonic-gate ret5:
31420Sstevel@tonic-gate 	if (t5.malloced) big_finish(&t5);
31430Sstevel@tonic-gate ret4:
31440Sstevel@tonic-gate 	if (t4.malloced) big_finish(&t4);
31450Sstevel@tonic-gate ret3:
31460Sstevel@tonic-gate 	if (t3.malloced) big_finish(&t3);
31470Sstevel@tonic-gate ret2:
31480Sstevel@tonic-gate 	if (t2.malloced) big_finish(&t2);
31490Sstevel@tonic-gate ret1:
31500Sstevel@tonic-gate 	if (t1.malloced) big_finish(&t1);
31510Sstevel@tonic-gate 
31520Sstevel@tonic-gate 	return (err);
31530Sstevel@tonic-gate }
315412573SDina.Nimeh@Sun.COM 
315512573SDina.Nimeh@Sun.COM /*
315612573SDina.Nimeh@Sun.COM  * Get a rlen-bit random number in BIGNUM format.  Caller-supplied
315712573SDina.Nimeh@Sun.COM  * (*rfunc)(void *dbuf, size_t dlen) must return 0 for success and
315812573SDina.Nimeh@Sun.COM  * -1 for failure.  Note:  (*rfunc)() takes length in bytes, not bits.
315912573SDina.Nimeh@Sun.COM  */
316012573SDina.Nimeh@Sun.COM BIG_ERR_CODE
316112573SDina.Nimeh@Sun.COM big_random(BIGNUM *r, size_t rlen, int (*rfunc)(void *, size_t))
316212573SDina.Nimeh@Sun.COM {
316312573SDina.Nimeh@Sun.COM 	size_t	rwords, rbytes;
316412573SDina.Nimeh@Sun.COM 	int	shift;
316512573SDina.Nimeh@Sun.COM 
316612573SDina.Nimeh@Sun.COM 	if (r == NULL || rlen == 0 || rfunc == NULL)
316712573SDina.Nimeh@Sun.COM 		return (BIG_INVALID_ARGS);
316812573SDina.Nimeh@Sun.COM 
316912573SDina.Nimeh@Sun.COM 	/*
317012573SDina.Nimeh@Sun.COM 	 * Convert rlen bits to r->len words (32- or 64-bit), rbytes bytes
317112573SDina.Nimeh@Sun.COM 	 * and extend r if it's not big enough to hold the random number.
317212573SDina.Nimeh@Sun.COM 	 */
317312573SDina.Nimeh@Sun.COM 	rwords = BITLEN2BIGNUMLEN(rlen);
317412573SDina.Nimeh@Sun.COM 	rbytes = rwords * sizeof (BIG_CHUNK_TYPE);
317512573SDina.Nimeh@Sun.COM 	if (big_extend(r, rwords) != BIG_OK)
317612573SDina.Nimeh@Sun.COM 		return (BIG_NO_MEM);
317712573SDina.Nimeh@Sun.COM #ifdef BIGNUM_CHUNK_32
317812573SDina.Nimeh@Sun.COM 	r->len = rwords;
317912573SDina.Nimeh@Sun.COM #else
318012573SDina.Nimeh@Sun.COM 	r->len = (uint32_t)rwords;
318112573SDina.Nimeh@Sun.COM #endif
318212573SDina.Nimeh@Sun.COM 
318312573SDina.Nimeh@Sun.COM 	if ((*rfunc)(r->value, rbytes) < 0)
318412573SDina.Nimeh@Sun.COM 		return (BIG_NO_RANDOM);
318512573SDina.Nimeh@Sun.COM 
318612573SDina.Nimeh@Sun.COM 	r->value[rwords - 1] |= BIG_CHUNK_HIGHBIT;
318712573SDina.Nimeh@Sun.COM 
318812573SDina.Nimeh@Sun.COM 	/*
318912573SDina.Nimeh@Sun.COM 	 * If the bit length is not a word boundary, shift the most
319012573SDina.Nimeh@Sun.COM 	 * significant word so that we have an exactly rlen-long number.
319112573SDina.Nimeh@Sun.COM 	 */
319212573SDina.Nimeh@Sun.COM 	if ((shift = rlen % BIG_CHUNK_SIZE) != 0)
319312573SDina.Nimeh@Sun.COM 		r->value[rwords - 1] >>= (BIG_CHUNK_SIZE - shift);
319412573SDina.Nimeh@Sun.COM 
319512573SDina.Nimeh@Sun.COM 	r->sign = 1;	/* non-negative */
319612573SDina.Nimeh@Sun.COM 
319712573SDina.Nimeh@Sun.COM 	return (BIG_OK);
319812573SDina.Nimeh@Sun.COM }
3199