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