Files
openssl/crypto/fn/fn_addsub.c
Richard Levitte 9043e50a39 OSSL_FN: Refactor OSSL_FN_add() and OSSL_FN_sub() for truncation
OSSL_FN_mul() set a path that wasn't considered for OSSL_FN_add() and
OSSL_FN_sub(); a truncated result if the result OSSL_FN isn't large
enough to contain the full result.

This is done to keep the OSSL_FN API consistent, with a (tentative)
bonus, that the function calls become more constant time accross
repeated calls with the same size for operands and result.

Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/29309)
2025-12-17 13:16:00 +01:00

163 lines
4.5 KiB
C

/*
* Copyright 1995-2025 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include "internal/cryptlib.h"
#include "crypto/fnerr.h"
#include "../bn/bn_local.h" /* For using the low level bignum functions */
#include "fn_local.h"
/* add of b to a. */
/* unsigned add of b to a, r can be equal to a or b. */
int OSSL_FN_add(OSSL_FN *r, const OSSL_FN *a, const OSSL_FN *b)
{
/*
* Addition is commutative, so we switch 'a' and 'b' around to
* ensure that 'a' is physically the largest, so a maximum of
* work is done with 'bn_add_words'
*/
if (a->dsize < b->dsize) {
const OSSL_FN *tmp;
tmp = a;
a = b;
b = tmp;
}
/* At this point, we know that a->dsize >= b->dsize */
size_t max = a->dsize;
size_t min = b->dsize;
size_t rs = r->dsize;
const OSSL_FN_ULONG *ap = a->d;
const OSSL_FN_ULONG *bp = b->d;
OSSL_FN_ULONG *rp = r->d;
/*
* Three stages, after which there's a possible return.
* Each stage is limited by the number of remaining limbs
* in |r|.
*
* For each stage, |stage_limbs| is used to hold the number
* of limbs being treated in that stage, and |borrow| is
* used to transport the borrow from one stage to the other.
*/
size_t stage_limbs;
OSSL_FN_ULONG carry;
/* Stage 1 */
stage_limbs = (min > rs) ? rs : min;
carry = bn_add_words(rp, ap, bp, (int)stage_limbs);
if (stage_limbs == rs)
return 1;
/* At this point, we know that |min| limbs have been used so far */
rp += min;
ap += min;
/* Stage 2 */
stage_limbs = ((max > rs) ? rs : max) - min;
for (size_t dif = stage_limbs; dif > 0; dif--, ap++, rp++) {
OSSL_FN_ULONG t1 = *ap;
OSSL_FN_ULONG t2 = (t1 + carry) & OSSL_FN_MASK;
*rp = t2;
carry &= (t2 == 0);
}
if (stage_limbs == rs)
return 1;
/* Stage 3 */
/* We know that |max| limbs have been used */
stage_limbs = rs - max;
for (size_t dif = stage_limbs; dif > 0; dif--, rp++) {
OSSL_FN_ULONG t1 = 0;
OSSL_FN_ULONG t2 = (t1 + carry) & OSSL_FN_MASK;
*rp = carry;
carry &= (t2 == 0);
}
return 1;
}
/* unsigned subtraction of b from a */
int OSSL_FN_sub(OSSL_FN *r, const OSSL_FN *a, const OSSL_FN *b)
{
size_t max = (a->dsize >= b->dsize) ? a->dsize : b->dsize;
size_t min = (a->dsize <= b->dsize) ? a->dsize : b->dsize;
size_t rs = r->dsize;
const OSSL_FN_ULONG *ap = a->d;
const OSSL_FN_ULONG *bp = b->d;
OSSL_FN_ULONG *rp = r->d;
/*
* Three stages, after which there's a possible return.
* Each stage is limited by the number of remaining limbs
* in |r|.
*
* For each stage, |stage_limbs| is used to hold the number
* of limbs being treated in that stage, and |borrow| is
* used to transport the borrow from one stage to the other.
*/
size_t stage_limbs;
OSSL_FN_ULONG borrow;
/* Stage 1 */
stage_limbs = (min > rs) ? rs : min;
borrow = bn_sub_words(rp, ap, bp, (int)stage_limbs);
if (stage_limbs == rs)
return 1;
/* At this point, we know that |min| limbs have been used so far */
ap += min;
bp += min;
rp += min;
/* Stage 2 */
const OSSL_FN_ULONG *maxp = (a->dsize >= b->dsize) ? ap : bp;
const OSSL_FN_ULONG s2_mask1 = (a->dsize >= b->dsize) ? OSSL_FN_MASK : 0;
const OSSL_FN_ULONG s2_mask2 = ~s2_mask1;
stage_limbs = ((max > rs) ? rs : max) - min;
/* calculate the result of borrowing from more significant limbs */
for (size_t dif = stage_limbs; dif > 0; dif--, maxp++, rp++) {
OSSL_FN_ULONG t1 = (*maxp & s2_mask1);
OSSL_FN_ULONG t2 = (*maxp & s2_mask2);
OSSL_FN_ULONG t3 = (t1 - t2 - borrow) & OSSL_FN_MASK;
*rp = t3;
borrow &= (t1 <= t2);
}
if (stage_limbs == rs)
return 1;
/* Stage 3 */
/* We know that |max| limbs have been used */
stage_limbs = rs - max;
/* Finally, fill in the rest of the result array by borrowing from zeros */
for (size_t dif = stage_limbs; dif > 0; dif--, rp++) {
OSSL_FN_ULONG t1 = 0;
OSSL_FN_ULONG t2 = (t1 - borrow) & OSSL_FN_MASK;
*rp = t2;
borrow &= (t1 == 0);
}
return 1;
}