xref: /openbsd-src/lib/libcrypto/bn/bn_shift.c (revision ca1d80d6bbf2924146ce99d3e4f20986ce6ddd0e)
1*ca1d80d6Sbeck /* $OpenBSD: bn_shift.c,v 1.22 2023/07/08 12:21:58 beck Exp $ */
2dfffc42aSjsing /*
3dfffc42aSjsing  * Copyright (c) 2022, 2023 Joel Sing <jsing@openbsd.org>
45b37fcf3Sryker  *
5dfffc42aSjsing  * Permission to use, copy, modify, and distribute this software for any
6dfffc42aSjsing  * purpose with or without fee is hereby granted, provided that the above
7dfffc42aSjsing  * copyright notice and this permission notice appear in all copies.
85b37fcf3Sryker  *
9dfffc42aSjsing  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10dfffc42aSjsing  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11dfffc42aSjsing  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12dfffc42aSjsing  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13dfffc42aSjsing  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14dfffc42aSjsing  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15dfffc42aSjsing  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
165b37fcf3Sryker  */
175b37fcf3Sryker 
18dfffc42aSjsing #include <openssl/bn.h>
19a990b40eStb #include <openssl/err.h>
20a990b40eStb 
21c9675a23Stb #include "bn_local.h"
225b37fcf3Sryker 
23dfffc42aSjsing static inline int
bn_lshift(BIGNUM * r,const BIGNUM * a,int n)24dfffc42aSjsing bn_lshift(BIGNUM *r, const BIGNUM *a, int n)
255b37fcf3Sryker {
26b331d0d1Sjsing 	size_t count, shift_bits, shift_words;
27b331d0d1Sjsing 	size_t lshift, rshift;
28b331d0d1Sjsing 	ssize_t rstride;
29b331d0d1Sjsing 	BN_ULONG *dst, *src;
305b37fcf3Sryker 
31a990b40eStb 	if (n < 0) {
32a990b40eStb 		BNerror(BN_R_INVALID_LENGTH);
33a990b40eStb 		return 0;
34a990b40eStb 	}
35b331d0d1Sjsing 	shift_bits = n;
36a990b40eStb 
37b331d0d1Sjsing 	/*
38b331d0d1Sjsing 	 * Left bit shift, potentially across word boundaries.
39b331d0d1Sjsing 	 *
40b331d0d1Sjsing 	 * When shift is not an exact multiple of BN_BITS2, the bottom bits of
41b331d0d1Sjsing 	 * the previous word need to be right shifted and combined with the left
42b331d0d1Sjsing 	 * shifted bits using bitwise OR. If shift is an exact multiple of
43b331d0d1Sjsing 	 * BN_BITS2, the source for the left and right shifts are the same
44b331d0d1Sjsing 	 * and the shifts become zero bits (which is effectively a memmove).
45b331d0d1Sjsing 	 */
46b331d0d1Sjsing 	shift_words = shift_bits / BN_BITS2;
47b331d0d1Sjsing 	lshift = shift_bits % BN_BITS2;
48b331d0d1Sjsing 	rshift = (BN_BITS2 - lshift) % BN_BITS2;
49b331d0d1Sjsing 	rstride = 0 - (lshift + rshift) / BN_BITS2;
504fcf65c5Sdjm 
51b331d0d1Sjsing 	if (a->top < 1) {
52b331d0d1Sjsing 		BN_zero(r);
53b331d0d1Sjsing 		return 1;
545b37fcf3Sryker 	}
55b331d0d1Sjsing 
56b331d0d1Sjsing 	count = a->top + shift_words + 1;
57b331d0d1Sjsing 
58b331d0d1Sjsing 	if (count < shift_words)
59b331d0d1Sjsing 		return 0;
60b331d0d1Sjsing 
61b331d0d1Sjsing 	if (!bn_wexpand(r, count))
62b331d0d1Sjsing 		return 0;
63b331d0d1Sjsing 
64b331d0d1Sjsing 	src = a->d + a->top - 1;
65b331d0d1Sjsing 	dst = r->d + a->top + shift_words;
66b331d0d1Sjsing 
67b331d0d1Sjsing 	/* Handle right shift for top most word. */
68b331d0d1Sjsing 	*dst = (*src >> rshift) & rstride;
69b331d0d1Sjsing 	dst--;
70b331d0d1Sjsing 
71b331d0d1Sjsing 	/* Handle left shift and right shift for remaining words. */
72b331d0d1Sjsing 	while (src > a->d) {
73b331d0d1Sjsing 		*dst = *src << lshift | src[rstride] >> rshift;
74b331d0d1Sjsing 		src--;
75b331d0d1Sjsing 		dst--;
76b331d0d1Sjsing 	}
77b331d0d1Sjsing 	*dst = *src << lshift;
78b331d0d1Sjsing 
79b331d0d1Sjsing 	/* Zero any additional words resulting from the left shift. */
80b331d0d1Sjsing 	while (dst > r->d) {
81b331d0d1Sjsing 		dst--;
82b331d0d1Sjsing 		*dst = 0;
83b331d0d1Sjsing 	}
84b331d0d1Sjsing 
85b331d0d1Sjsing 	r->top = count;
864fcf65c5Sdjm 	bn_correct_top(r);
87b331d0d1Sjsing 
88896da13fSjsing 	BN_set_negative(r, a->neg);
89896da13fSjsing 
90b331d0d1Sjsing 	return 1;
915b37fcf3Sryker }
925b37fcf3Sryker 
93dfffc42aSjsing static inline int
bn_rshift(BIGNUM * r,const BIGNUM * a,int n)94dfffc42aSjsing bn_rshift(BIGNUM *r, const BIGNUM *a, int n)
955b37fcf3Sryker {
9614a120fbSjsing 	size_t count, shift_bits, shift_words;
9714a120fbSjsing 	size_t lshift, rshift;
9814a120fbSjsing 	ssize_t lstride;
9914a120fbSjsing 	BN_ULONG *dst, *src;
10014a120fbSjsing 	size_t i;
1015b37fcf3Sryker 
102a990b40eStb 	if (n < 0) {
103a990b40eStb 		BNerror(BN_R_INVALID_LENGTH);
104a990b40eStb 		return 0;
105a990b40eStb 	}
10614a120fbSjsing 	shift_bits = n;
107a990b40eStb 
10814a120fbSjsing 	/*
10914a120fbSjsing 	 * Right bit shift, potentially across word boundaries.
11014a120fbSjsing 	 *
11114a120fbSjsing 	 * When shift is not an exact multiple of BN_BITS2, the top bits of
11214a120fbSjsing 	 * the next word need to be left shifted and combined with the right
11314a120fbSjsing 	 * shifted bits using bitwise OR. If shift is an exact multiple of
11414a120fbSjsing 	 * BN_BITS2, the source for the left and right shifts are the same
11514a120fbSjsing 	 * and the shifts become zero (which is effectively a memmove).
11614a120fbSjsing 	 */
11714a120fbSjsing 	shift_words = shift_bits / BN_BITS2;
11814a120fbSjsing 	rshift = shift_bits % BN_BITS2;
11914a120fbSjsing 	lshift = (BN_BITS2 - rshift) % BN_BITS2;
12014a120fbSjsing 	lstride = (lshift + rshift) / BN_BITS2;
1214fcf65c5Sdjm 
12214a120fbSjsing 	if (a->top <= shift_words) {
1235b37fcf3Sryker 		BN_zero(r);
12414a120fbSjsing 		return 1;
1255b37fcf3Sryker 	}
12614a120fbSjsing 	count = a->top - shift_words;
12714a120fbSjsing 
12814a120fbSjsing 	if (!bn_wexpand(r, count))
12914a120fbSjsing 		return 0;
13014a120fbSjsing 
13114a120fbSjsing 	src = a->d + shift_words;
13214a120fbSjsing 	dst = r->d;
13314a120fbSjsing 
13414a120fbSjsing 	for (i = 1; i < count; i++) {
13514a120fbSjsing 		*dst = src[lstride] << lshift | *src >> rshift;
13614a120fbSjsing 		src++;
13714a120fbSjsing 		dst++;
13814a120fbSjsing 	}
13914a120fbSjsing 	*dst = *src >> rshift;
14014a120fbSjsing 
14114a120fbSjsing 	r->top = count;
14214a120fbSjsing 	bn_correct_top(r);
1435b37fcf3Sryker 
144896da13fSjsing 	BN_set_negative(r, a->neg);
145896da13fSjsing 
14614a120fbSjsing 	return 1;
1475b37fcf3Sryker }
148dfffc42aSjsing 
149dfffc42aSjsing int
BN_lshift1(BIGNUM * r,const BIGNUM * a)150dfffc42aSjsing BN_lshift1(BIGNUM *r, const BIGNUM *a)
151dfffc42aSjsing {
152dfffc42aSjsing 	return bn_lshift(r, a, 1);
153dfffc42aSjsing }
154*ca1d80d6Sbeck LCRYPTO_ALIAS(BN_lshift1);
155dfffc42aSjsing 
156dfffc42aSjsing int
BN_lshift(BIGNUM * r,const BIGNUM * a,int n)157dfffc42aSjsing BN_lshift(BIGNUM *r, const BIGNUM *a, int n)
158dfffc42aSjsing {
159dfffc42aSjsing 	return bn_lshift(r, a, n);
160dfffc42aSjsing }
161*ca1d80d6Sbeck LCRYPTO_ALIAS(BN_lshift);
162dfffc42aSjsing 
163dfffc42aSjsing int
BN_rshift1(BIGNUM * r,const BIGNUM * a)164dfffc42aSjsing BN_rshift1(BIGNUM *r, const BIGNUM *a)
165dfffc42aSjsing {
166dfffc42aSjsing 	return bn_rshift(r, a, 1);
167dfffc42aSjsing }
168*ca1d80d6Sbeck LCRYPTO_ALIAS(BN_rshift1);
169dfffc42aSjsing 
170dfffc42aSjsing int
BN_rshift(BIGNUM * r,const BIGNUM * a,int n)171dfffc42aSjsing BN_rshift(BIGNUM *r, const BIGNUM *a, int n)
172dfffc42aSjsing {
173dfffc42aSjsing 	return bn_rshift(r, a, n);
174dfffc42aSjsing }
175*ca1d80d6Sbeck LCRYPTO_ALIAS(BN_rshift);
176