xref: /openbsd/lib/libcrypto/bn/bn_recp.c (revision b4a69cdb)
1*b4a69cdbStb /* $OpenBSD: bn_recp.c,v 1.30 2025/01/22 12:53:16 tb Exp $ */
25b37fcf3Sryker /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
35b37fcf3Sryker  * All rights reserved.
45b37fcf3Sryker  *
55b37fcf3Sryker  * This package is an SSL implementation written
65b37fcf3Sryker  * by Eric Young (eay@cryptsoft.com).
75b37fcf3Sryker  * The implementation was written so as to conform with Netscapes SSL.
85b37fcf3Sryker  *
95b37fcf3Sryker  * This library is free for commercial and non-commercial use as long as
105b37fcf3Sryker  * the following conditions are aheared to.  The following conditions
115b37fcf3Sryker  * apply to all code found in this distribution, be it the RC4, RSA,
125b37fcf3Sryker  * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
135b37fcf3Sryker  * included with this distribution is covered by the same copyright terms
145b37fcf3Sryker  * except that the holder is Tim Hudson (tjh@cryptsoft.com).
155b37fcf3Sryker  *
165b37fcf3Sryker  * Copyright remains Eric Young's, and as such any Copyright notices in
175b37fcf3Sryker  * the code are not to be removed.
185b37fcf3Sryker  * If this package is used in a product, Eric Young should be given attribution
195b37fcf3Sryker  * as the author of the parts of the library used.
205b37fcf3Sryker  * This can be in the form of a textual message at program startup or
215b37fcf3Sryker  * in documentation (online or textual) provided with the package.
225b37fcf3Sryker  *
235b37fcf3Sryker  * Redistribution and use in source and binary forms, with or without
245b37fcf3Sryker  * modification, are permitted provided that the following conditions
255b37fcf3Sryker  * are met:
265b37fcf3Sryker  * 1. Redistributions of source code must retain the copyright
275b37fcf3Sryker  *    notice, this list of conditions and the following disclaimer.
285b37fcf3Sryker  * 2. Redistributions in binary form must reproduce the above copyright
295b37fcf3Sryker  *    notice, this list of conditions and the following disclaimer in the
305b37fcf3Sryker  *    documentation and/or other materials provided with the distribution.
315b37fcf3Sryker  * 3. All advertising materials mentioning features or use of this software
325b37fcf3Sryker  *    must display the following acknowledgement:
335b37fcf3Sryker  *    "This product includes cryptographic software written by
345b37fcf3Sryker  *     Eric Young (eay@cryptsoft.com)"
355b37fcf3Sryker  *    The word 'cryptographic' can be left out if the rouines from the library
365b37fcf3Sryker  *    being used are not cryptographic related :-).
375b37fcf3Sryker  * 4. If you include any Windows specific code (or a derivative thereof) from
385b37fcf3Sryker  *    the apps directory (application code) you must include an acknowledgement:
395b37fcf3Sryker  *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
405b37fcf3Sryker  *
415b37fcf3Sryker  * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
425b37fcf3Sryker  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
435b37fcf3Sryker  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
445b37fcf3Sryker  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
455b37fcf3Sryker  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
465b37fcf3Sryker  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
475b37fcf3Sryker  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
485b37fcf3Sryker  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
495b37fcf3Sryker  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
505b37fcf3Sryker  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
515b37fcf3Sryker  * SUCH DAMAGE.
525b37fcf3Sryker  *
535b37fcf3Sryker  * The licence and distribution terms for any publically available version or
545b37fcf3Sryker  * derivative of this code cannot be changed.  i.e. this code cannot simply be
555b37fcf3Sryker  * copied and put under another distribution licence
565b37fcf3Sryker  * [including the GNU Public Licence.]
575b37fcf3Sryker  */
585b37fcf3Sryker 
595b37fcf3Sryker #include <stdio.h>
60b6ab114eSjsing 
61b6ab114eSjsing #include <openssl/err.h>
62b6ab114eSjsing 
63c9675a23Stb #include "bn_local.h"
645b37fcf3Sryker 
6505328004Stb struct bn_recp_ctx_st {
6605328004Stb 	BIGNUM *N;	/* the divisor */
6705328004Stb 	BIGNUM *Nr;	/* the reciprocal 2^shift / N */
6805328004Stb 	int num_bits;	/* number of bits in N */
6905328004Stb 	int shift;
7005328004Stb } /* BN_RECP_CTX */;
715b37fcf3Sryker 
722bd9bb84Sjsing BN_RECP_CTX *
BN_RECP_CTX_create(const BIGNUM * N)7305328004Stb BN_RECP_CTX_create(const BIGNUM *N)
74913ec974Sbeck {
7505328004Stb 	BN_RECP_CTX *recp;
765b37fcf3Sryker 
7705328004Stb 	if ((recp = calloc(1, sizeof(*recp))) == NULL)
7805328004Stb 		goto err;
7905328004Stb 
8005328004Stb 	if ((recp->N = BN_dup(N)) == NULL)
8105328004Stb 		goto err;
82*b4a69cdbStb 	BN_set_negative(recp->N, 0);
8305328004Stb 	recp->num_bits = BN_num_bits(recp->N);
8405328004Stb 
8505328004Stb 	if ((recp->Nr = BN_new()) == NULL)
8605328004Stb 		goto err;
8705328004Stb 
8805328004Stb 	return recp;
8905328004Stb 
9005328004Stb  err:
9105328004Stb 	BN_RECP_CTX_free(recp);
9205328004Stb 
9359c697bfStb 	return NULL;
94913ec974Sbeck }
95913ec974Sbeck 
962bd9bb84Sjsing void
BN_RECP_CTX_free(BN_RECP_CTX * recp)972bd9bb84Sjsing BN_RECP_CTX_free(BN_RECP_CTX *recp)
98913ec974Sbeck {
99913ec974Sbeck 	if (recp == NULL)
100913ec974Sbeck 		return;
101913ec974Sbeck 
10205328004Stb 	BN_free(recp->N);
10305328004Stb 	BN_free(recp->Nr);
10405328004Stb 	freezero(recp, sizeof(*recp));
105913ec974Sbeck }
106913ec974Sbeck 
107abfe84ecStb /* len is the expected size of the result
108abfe84ecStb  * We actually calculate with an extra word of precision, so
109abfe84ecStb  * we can do faster division if the remainder is not required.
110abfe84ecStb  */
111abfe84ecStb /* r := 2^len / m */
112abfe84ecStb static int
BN_reciprocal(BIGNUM * r,const BIGNUM * m,int len,BN_CTX * ctx)113abfe84ecStb BN_reciprocal(BIGNUM *r, const BIGNUM *m, int len, BN_CTX *ctx)
114913ec974Sbeck {
115abfe84ecStb 	int ret = -1;
116abfe84ecStb 	BIGNUM *t;
117913ec974Sbeck 
118ba5406e9Sbeck 	BN_CTX_start(ctx);
119abfe84ecStb 	if ((t = BN_CTX_get(ctx)) == NULL)
1202bd9bb84Sjsing 		goto err;
121913ec974Sbeck 
122abfe84ecStb 	if (!BN_set_bit(t, len))
123abfe84ecStb 		goto err;
124abfe84ecStb 
125abfe84ecStb 	if (!BN_div_ct(r, NULL, t, m, ctx))
126abfe84ecStb 		goto err;
127abfe84ecStb 
128abfe84ecStb 	ret = len;
1292bd9bb84Sjsing 
130913ec974Sbeck err:
131ba5406e9Sbeck 	BN_CTX_end(ctx);
13259c697bfStb 	return ret;
133913ec974Sbeck }
134913ec974Sbeck 
1359d6e4a9aStb int
BN_div_reciprocal(BIGNUM * dv,BIGNUM * rem,const BIGNUM * m,BN_RECP_CTX * recp,BN_CTX * ctx)13647537ea9Stb BN_div_reciprocal(BIGNUM *dv, BIGNUM *rem, const BIGNUM *m, BN_RECP_CTX *recp,
1372bd9bb84Sjsing     BN_CTX *ctx)
138913ec974Sbeck {
139ba5406e9Sbeck 	int i, j, ret = 0;
140913ec974Sbeck 	BIGNUM *a, *b, *d, *r;
141913ec974Sbeck 
142ba5406e9Sbeck 	BN_CTX_start(ctx);
143ba5406e9Sbeck 	a = BN_CTX_get(ctx);
144ba5406e9Sbeck 	b = BN_CTX_get(ctx);
145913ec974Sbeck 	if (dv != NULL)
146913ec974Sbeck 		d = dv;
147913ec974Sbeck 	else
148ba5406e9Sbeck 		d = BN_CTX_get(ctx);
149913ec974Sbeck 	if (rem != NULL)
150913ec974Sbeck 		r = rem;
151913ec974Sbeck 	else
152ba5406e9Sbeck 		r = BN_CTX_get(ctx);
1532bd9bb84Sjsing 	if (a == NULL || b == NULL || d == NULL || r == NULL)
1542bd9bb84Sjsing 		goto err;
155913ec974Sbeck 
15605328004Stb 	if (BN_ucmp(m, recp->N) < 0) {
1574fcf65c5Sdjm 		BN_zero(d);
158cef8f41dStb 		if (!bn_copy(r, m)) {
1594c1eb2ebSdoug 			BN_CTX_end(ctx);
1602bd9bb84Sjsing 			return 0;
1614c1eb2ebSdoug 		}
162ba5406e9Sbeck 		BN_CTX_end(ctx);
16359c697bfStb 		return 1;
164913ec974Sbeck 	}
165913ec974Sbeck 
166913ec974Sbeck 	/* We want the remainder
167913ec974Sbeck 	 * Given input of ABCDEF / ab
168913ec974Sbeck 	 * we need multiply ABCDEF by 3 digests of the reciprocal of ab
169913ec974Sbeck 	 *
170913ec974Sbeck 	 */
171913ec974Sbeck 
172da347917Sbeck 	/* i := max(BN_num_bits(m), 2*BN_num_bits(N)) */
173da347917Sbeck 	i = BN_num_bits(m);
174ba5406e9Sbeck 	j = recp->num_bits << 1;
1752bd9bb84Sjsing 	if (j > i)
1762bd9bb84Sjsing 		i = j;
177913ec974Sbeck 
178da347917Sbeck 	/* Nr := round(2^i / N) */
179913ec974Sbeck 	if (i != recp->shift)
18005328004Stb 		recp->shift = BN_reciprocal(recp->Nr, recp->N, i, ctx);
181bb2f0c56Sdoug 
182bb2f0c56Sdoug 	/* BN_reciprocal returns i, or -1 for an error */
1832bd9bb84Sjsing 	if (recp->shift == -1)
1842bd9bb84Sjsing 		goto err;
185913ec974Sbeck 
186da347917Sbeck 	/* d := |round(round(m / 2^BN_num_bits(N)) * recp->Nr / 2^(i - BN_num_bits(N)))|
187da347917Sbeck 	 *    = |round(round(m / 2^BN_num_bits(N)) * round(2^i / N) / 2^(i - BN_num_bits(N)))|
188da347917Sbeck 	 *   <= |(m / 2^BN_num_bits(N)) * (2^i / N) * (2^BN_num_bits(N) / 2^i)|
189da347917Sbeck 	 *    = |m/N|
190da347917Sbeck 	 */
1912bd9bb84Sjsing 	if (!BN_rshift(a, m, recp->num_bits))
1922bd9bb84Sjsing 		goto err;
19305328004Stb 	if (!BN_mul(b, a, recp->Nr, ctx))
1942bd9bb84Sjsing 		goto err;
1952bd9bb84Sjsing 	if (!BN_rshift(d, b, i - recp->num_bits))
1962bd9bb84Sjsing 		goto err;
197913ec974Sbeck 	d->neg = 0;
198da347917Sbeck 
19905328004Stb 	if (!BN_mul(b, recp->N, d, ctx))
2002bd9bb84Sjsing 		goto err;
2012bd9bb84Sjsing 	if (!BN_usub(r, m, b))
2022bd9bb84Sjsing 		goto err;
203913ec974Sbeck 	r->neg = 0;
204913ec974Sbeck 
205913ec974Sbeck #if 1
206ba5406e9Sbeck 	j = 0;
20705328004Stb 	while (BN_ucmp(r, recp->N) >= 0) {
2082bd9bb84Sjsing 		if (j++ > 2) {
2095067ae9fSbeck 			BNerror(BN_R_BAD_RECIPROCAL);
2105b37fcf3Sryker 			goto err;
2115b37fcf3Sryker 		}
21205328004Stb 		if (!BN_usub(r, r, recp->N))
2132bd9bb84Sjsing 			goto err;
2142bd9bb84Sjsing 		if (!BN_add_word(d, 1))
2152bd9bb84Sjsing 			goto err;
2165b37fcf3Sryker 	}
217913ec974Sbeck #endif
2185b37fcf3Sryker 
219896da13fSjsing 	BN_set_negative(r, m->neg);
22005328004Stb 	BN_set_negative(d, m->neg ^ recp->N->neg);
221896da13fSjsing 
2225b37fcf3Sryker 	ret = 1;
2232bd9bb84Sjsing 
2245b37fcf3Sryker err:
225ba5406e9Sbeck 	BN_CTX_end(ctx);
22659c697bfStb 	return ret;
2275b37fcf3Sryker }
2285b37fcf3Sryker 
229e9711763Stb /* Compute r = (x * y) % m. */
2302bd9bb84Sjsing int
BN_mod_mul_reciprocal(BIGNUM * r,const BIGNUM * x,const BIGNUM * y,BN_RECP_CTX * recp,BN_CTX * ctx)231abfe84ecStb BN_mod_mul_reciprocal(BIGNUM *r, const BIGNUM *x, const BIGNUM *y,
232abfe84ecStb     BN_RECP_CTX *recp, BN_CTX *ctx)
2335b37fcf3Sryker {
23402fba168Stb 	if (!BN_mul(r, x, y, ctx))
23502fba168Stb 		return 0;
2365b37fcf3Sryker 
23747537ea9Stb 	return BN_div_reciprocal(NULL, r, r, recp, ctx);
238abfe84ecStb }
239da347917Sbeck 
24002fba168Stb /* Compute r = x^2 % m. */
24102fba168Stb int
BN_mod_sqr_reciprocal(BIGNUM * r,const BIGNUM * x,BN_RECP_CTX * recp,BN_CTX * ctx)24202fba168Stb BN_mod_sqr_reciprocal(BIGNUM *r, const BIGNUM *x, BN_RECP_CTX *recp, BN_CTX *ctx)
24302fba168Stb {
24402fba168Stb 	if (!BN_sqr(r, x, ctx))
24502fba168Stb 		return 0;
2462bd9bb84Sjsing 
24747537ea9Stb 	return BN_div_reciprocal(NULL, r, r, recp, ctx);
2485b37fcf3Sryker }
249