blob: 38076a1739949a2a5fba3e94cead07c76f93a5ed [file] [log] [blame]
/* Microsoft Reference Implementation for TPM 2.0
*
* The copyright in this software is being made available under the BSD License,
* included below. This software may be subject to other third party and
* contributor rights, including patent rights, and no such rights are granted
* under this license.
*
* Copyright (c) Microsoft Corporation
*
* All rights reserved.
*
* BSD License
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list
* of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS""
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//** Introduction
//
// This file contains the math functions that are not implemented in the BnMath
// library (yet). These math functions will call the OpenSSL library to execute
// the operations. There is a difference between the internal format and the
// OpenSSL format. To call the OpenSSL function, a BIGNUM structure is created
// for each passed variable. The sizes in the bignum_t are copied and the 'd'
// pointer in the BIGNUM is set to point to the 'd' parameter of the bignum_t.
// On return, SetSizeOsslToTpm is used for each returned variable to make sure that
// the pointers are not changed. The size of the returned BIGGNUM is copied to
// bignum_t.
//** Includes and Defines
#include "Tpm.h"
#if MATH_LIB == OSSL
#include "TpmToOsslMath_fp.h"
//** Functions
//*** OsslToTpmBn()
// This function converts an OpenSSL BIGNUM to a TPM bignum. In this implementation
// it is assumed that OpenSSL used the same format for a big number as does the
// TPM -- an array of native-endian words in little-endian order.
//
// If the array allocated for the OpenSSL BIGNUM is not the space within the TPM
// bignum, then the data is copied. Otherwise, just the size field of the BIGNUM
// is copied.
void
OsslToTpmBn(
bigNum bn,
BIGNUM *osslBn
)
{
if(bn != NULL)
{
if((crypt_uword_t *)osslBn->d != bn->d)
{
int i;
pAssert((unsigned)osslBn->top <= BnGetAllocated(bn));
for(i = 0; i < osslBn->top; i++)
bn->d[i] = osslBn->d[i];
}
BnSetTop(bn, osslBn->top);
}
}
//*** BigInitialized()
// This function initializes an OSSL BIGNUM from a TPM bignum.
BIGNUM *
BigInitialized(
BIGNUM *toInit,
bigConst initializer
)
{
if(toInit == NULL || initializer == NULL)
return NULL;
toInit->d = (BN_ULONG *)&initializer->d[0];
toInit->dmax = initializer->allocated;
toInit->top = initializer->size;
toInit->neg = 0;
toInit->flags = 0;
return toInit;
}
#ifndef OSSL_DEBUG
# define BIGNUM_PRINT(label, bn, eol)
# define DEBUG_PRINT(x)
#else
# define DEBUG_PRINT(x) printf("%s", x)
# define BIGNUM_PRINT(label, bn, eol) BIGNUM_print((label), (bn), (eol))
static
void BIGNUM_print(
const char *label,
const BIGNUM *a,
BOOL eol
)
{
BN_ULONG *d;
int i;
int notZero = FALSE;
if(label != NULL)
printf("%s", label);
if(a == NULL)
{
printf("NULL");
goto done;
}
if (a->neg)
printf("-");
for(i = a->top, d = &a->d[i - 1]; i > 0; i--)
{
int j;
BN_ULONG l = *d--;
for(j = BN_BITS2 - 8; j >= 0; j -= 8)
{
BYTE b = (BYTE)((l >> j) & 0xFF);
notZero = notZero || (b != 0);
if(notZero)
printf("%02x", b);
}
if(!notZero)
printf("0");
}
done:
if(eol)
printf("\n");
return;
}
#endif
#ifdef LIBRARY_COMPATIBILITY_CHECK
void
MathLibraryCompatibilityCheck(
void
)
{
OSSL_ENTER();
BIGNUM *osslTemp = BN_CTX_get(CTX);
BN_VAR(tpmTemp, 64 * 8); // allocate some space for a test value
crypt_uword_t i;
TPM2B_TYPE(TEST, 16);
TPM2B_TEST test = {{16, {0x0F, 0x0E, 0x0D, 0x0C,
0x0B, 0x0A, 0x09, 0x08,
0x07, 0x06, 0x05, 0x04,
0x03, 0x02, 0x01, 0x00}}};
// Convert the test TPM2B to a bigNum
BnFrom2B(tpmTemp, &test.b);
// Convert the test TPM2B to an OpenSSL BIGNUM
BN_bin2bn(test.t.buffer, test.t.size, osslTemp);
// Make sure the values are consistent
cAssert(osslTemp->top == (int)tpmTemp->size);
for(i = 0; i < tpmTemp->size; i++)
cAssert(osslTemp->d[0] == tpmTemp->d[0]);
OSSL_LEAVE();
}
#endif
//*** BnModMult()
// Does multiply and divide returning the remainder of the divide.
LIB_EXPORT BOOL
BnModMult(
bigNum result,
bigConst op1,
bigConst op2,
bigConst modulus
)
{
OSSL_ENTER();
BIG_INITIALIZED(bnResult, result);
BIG_INITIALIZED(bnOp1, op1);
BIG_INITIALIZED(bnOp2, op2);
BIG_INITIALIZED(bnMod, modulus);
BIG_VAR(bnTemp, (LARGEST_NUMBER_BITS * 4));
BOOL OK;
pAssert(BnGetAllocated(result) >= BnGetSize(modulus));
OK = BN_mul(bnTemp, bnOp1, bnOp2, CTX);
OK = OK && BN_div(NULL, bnResult, bnTemp, bnMod, CTX);
if(OK)
{
result->size = bnResult->top;
OsslToTpmBn(result, bnResult);
}
OSSL_LEAVE();
return OK;
}
//*** BnMult()
// Multiplies two numbers
LIB_EXPORT BOOL
BnMult(
bigNum result,
bigConst multiplicand,
bigConst multiplier
)
{
OSSL_ENTER();
BN_VAR(temp, (LARGEST_NUMBER_BITS * 2));
BIG_INITIALIZED(bnTemp, temp);
BIG_INITIALIZED(bnA, multiplicand);
BIG_INITIALIZED(bnB, multiplier);
BOOL OK;
pAssert(result->allocated >=
(BITS_TO_CRYPT_WORDS(BnSizeInBits(multiplicand)
+ BnSizeInBits(multiplier))));
OK = BN_mul(bnTemp, bnA, bnB, CTX);
if(OK)
{
OsslToTpmBn(temp, bnTemp);
BnCopy(result, temp);
}
OSSL_LEAVE();
return OK;
}
//*** BnDiv()
// This function divides two bigNum values. The function returns FALSE if
// there is an error in the operation.
LIB_EXPORT BOOL
BnDiv(
bigNum quotient,
bigNum remainder,
bigConst dividend,
bigConst divisor
)
{
OSSL_ENTER();
BIG_INITIALIZED(bnQ, quotient);
BIG_INITIALIZED(bnR, remainder);
BIG_INITIALIZED(bnDend, dividend);
BIG_INITIALIZED(bnSor, divisor);
BOOL OK;
pAssert(!BnEqualZero(divisor));
if(BnGetSize(dividend) < BnGetSize(divisor))
{
if(quotient)
BnSetWord(quotient, 0);
if(remainder)
BnCopy(remainder, dividend);
OK = TRUE;
}
else
{
pAssert((quotient == NULL)
|| (quotient->allocated >= (unsigned)(dividend->size
- divisor->size)));
pAssert((remainder == NULL)
|| (remainder->allocated >= divisor->size));
OK = BN_div(bnQ, bnR, bnDend, bnSor, CTX);
if(OK)
{
OsslToTpmBn(quotient, bnQ);
OsslToTpmBn(remainder, bnR);
}
}
DEBUG_PRINT("In BnDiv:\n");
BIGNUM_PRINT(" bnDividend: ", bnDend, TRUE);
BIGNUM_PRINT(" bnDivisor: ", bnSor, TRUE);
BIGNUM_PRINT(" bnQuotient: ", bnQ, TRUE);
BIGNUM_PRINT(" bnRemainder: ", bnR, TRUE);
OSSL_LEAVE();
return OK;
}
#ifdef TPM_ALG_RSA
//*** BnGcd()
// Get the greatest common divisor of two numbers
LIB_EXPORT BOOL
BnGcd(
bigNum gcd, // OUT: the common divisor
bigConst number1, // IN:
bigConst number2 // IN:
)
{
OSSL_ENTER();
BIG_INITIALIZED(bnGcd, gcd);
BIG_INITIALIZED(bn1, number1);
BIG_INITIALIZED(bn2, number2);
BOOL OK;
pAssert(gcd != NULL);
OK = BN_gcd(bnGcd, bn1, bn2, CTX);
if(OK)
{
OsslToTpmBn(gcd, bnGcd);
gcd->size = bnGcd->top;
}
OSSL_LEAVE();
return OK;
}
//***BnModExp()
// Do modular exponentiation using bigNum values. The conversion from a bignum_t to
// a bigNum is trivial as they are based on the same structure
LIB_EXPORT BOOL
BnModExp(
bigNum result, // OUT: the result
bigConst number, // IN: number to exponentiate
bigConst exponent, // IN:
bigConst modulus // IN:
)
{
OSSL_ENTER();
BIG_INITIALIZED(bnResult, result);
BIG_INITIALIZED(bnN, number);
BIG_INITIALIZED(bnE, exponent);
BIG_INITIALIZED(bnM, modulus);
BOOL OK;
//
OK = BN_mod_exp(bnResult, bnN, bnE, bnM, CTX);
if(OK)
{
OsslToTpmBn(result, bnResult);
}
OSSL_LEAVE();
return OK;
}
//*** BnModInverse()
// Modular multiplicative inverse
LIB_EXPORT BOOL
BnModInverse(
bigNum result,
bigConst number,
bigConst modulus
)
{
OSSL_ENTER();
BIG_INITIALIZED(bnResult, result);
BIG_INITIALIZED(bnN, number);
BIG_INITIALIZED(bnM, modulus);
BOOL OK;
OK = (BN_mod_inverse(bnResult, bnN, bnM, CTX) != NULL);
if(OK)
{
OsslToTpmBn(result, bnResult);
}
OSSL_LEAVE();
return OK;
}
#endif // TPM_ALG_RSA
#ifdef TPM_ALG_ECC
//*** PointFromOssl()
// Function to copy the point result from an OSSL function to a bigNum
static BOOL
PointFromOssl(
bigPoint pOut, // OUT: resulting point
EC_POINT *pIn, // IN: the point to return
bigCurve E // IN: the curve
)
{
BIGNUM *x = NULL;
BIGNUM *y = NULL;
BOOL OK;
BN_CTX_start(E->CTX);
//
x = BN_CTX_get(E->CTX);
y = BN_CTX_get(E->CTX);
if(y == NULL)
FAIL(FATAL_ERROR_ALLOCATION);
// If this returns false, then the point is at infinity
OK = EC_POINT_get_affine_coordinates_GFp(E->G, pIn, x, y, E->CTX);
if(OK)
{
OsslToTpmBn(pOut->x, x);
OsslToTpmBn(pOut->y, y);
BnSetWord(pOut->z, 1);
}
else
BnSetWord(pOut->z, 0);
BN_CTX_end(E->CTX);
return OK;
}
//*** EcPointInitialized()
// Allocate and initialize a point.
static EC_POINT *
EcPointInitialized(
pointConst initializer,
bigCurve E
)
{
BIG_INITIALIZED(bnX, (initializer != NULL) ? initializer->x : NULL);
BIG_INITIALIZED(bnY, (initializer != NULL) ? initializer->y : NULL);
EC_POINT *P = (initializer != NULL && E != NULL)
? EC_POINT_new(E->G) : NULL;
pAssert(E != NULL);
if(P != NULL)
EC_POINT_set_affine_coordinates_GFp(E->G, P, bnX, bnY, E->CTX);
return P;
}
//*** BnCurveInitialize()
// This function initializes the OpenSSL group definition
//
// It is a fatal error if 'groupContext' is not provided.
// return type: bigCurve *
// NULL the TPM_ECC_CURVE is not valid
// non-NULL points to a structure in 'groupContext'
bigCurve
BnCurveInitialize(
bigCurve E, // IN: curve structure to initialize
TPM_ECC_CURVE curveId // IN: curve identifier
)
{
EC_GROUP *group = NULL;
EC_POINT *P = NULL;
const ECC_CURVE_DATA *C = GetCurveData(curveId);
BN_CTX *CTX = NULL;
BIG_INITIALIZED(bnP, C != NULL ? C->prime : NULL);
BIG_INITIALIZED(bnA, C != NULL ? C->a : NULL);
BIG_INITIALIZED(bnB, C != NULL ? C->b : NULL);
BIG_INITIALIZED(bnX, C != NULL ? C->base.x : NULL);
BIG_INITIALIZED(bnY, C != NULL ? C->base.y : NULL);
BIG_INITIALIZED(bnN, C != NULL ? C->order : NULL);
BIG_INITIALIZED(bnH, C != NULL ? C->h : NULL);
int OK = (C != NULL);
//
OK = OK && ((CTX = OsslContextEnter()) != NULL);
// initialize EC group, associate a generator point and initialize the point
// from the parameter data
// Create a group structure
OK = OK && (group = EC_GROUP_new_curve_GFp(bnP, bnA, bnB, CTX)) != NULL;
// Allocate a point in the group that will be used in setting the
// generator. This is not needed after the generator is set.
OK = OK && ((P = EC_POINT_new(group)) != NULL);
// Need to use this in case Montgomery method is being used
OK = OK
&& EC_POINT_set_affine_coordinates_GFp(group, P, bnX, bnY, CTX);
// Now set the generator
OK = OK && EC_GROUP_set_generator(group, P, bnN, bnH);
if(P != NULL)
EC_POINT_free(P);
if(!OK && group != NULL)
{
EC_GROUP_free(group);
group = NULL;
}
if(!OK && CTX != NULL)
{
OsslContextLeave(CTX);
CTX = NULL;
}
E->G = group;
E->CTX = CTX;
E->C = C;
return OK ? E : NULL;
}
//*** BnEccModMult()
// This function does a point multiply of the form R = [d]S
// return type: BOOL
// FALSE failure in operation; treat as result being point at infinity
LIB_EXPORT BOOL
BnEccModMult(
bigPoint R, // OUT: computed point
pointConst S, // IN: point to multiply by 'd' (optional)
bigConst d, // IN: scalar for [d]S
bigCurve E
)
{
EC_POINT *pR = EC_POINT_new(E->G);
EC_POINT *pS = EcPointInitialized(S, E);
BIG_INITIALIZED(bnD, d);
if(S == NULL)
EC_POINT_mul(E->G, pR, bnD, NULL, NULL, E->CTX);
else
EC_POINT_mul(E->G, pR, NULL, pS, bnD, E->CTX);
PointFromOssl(R, pR, E);
EC_POINT_free(pR);
EC_POINT_free(pS);
return !BnEqualZero(R->z);
}
//*** BnEccModMult2()
// This function does a point multiply of the form R = [d]G + [u]Q
// return type: BOOL
// FALSE failure in operation; treat as result being point at infinity
LIB_EXPORT BOOL
BnEccModMult2(
bigPoint R, // OUT: computed point
pointConst S, // IN: optional point
bigConst d, // IN: scalar for [d]S or [d]G
pointConst Q, // IN: second point
bigConst u, // IN: second scalar
bigCurve E // IN: curve
)
{
EC_POINT *pR = EC_POINT_new(E->G);
EC_POINT *pS = EcPointInitialized(S, E);
BIG_INITIALIZED(bnD, d);
EC_POINT *pQ = EcPointInitialized(Q, E);
BIG_INITIALIZED(bnU, u);
if(S == NULL || S == (pointConst)&E->C->base)
EC_POINT_mul(E->G, pR, bnD, pQ, bnU, E->CTX);
else
{
const EC_POINT *points[2];
const BIGNUM *scalars[2];
points[0] = pS;
points[1] = pQ;
scalars[0] = bnD;
scalars[1] = bnU;
EC_POINTs_mul(E->G, pR, NULL, 2, points, scalars, E->CTX);
}
PointFromOssl(R, pR, E);
EC_POINT_free(pR);
EC_POINT_free(pS);
EC_POINT_free(pQ);
return !BnEqualZero(R->z);
}
//** BnEccAdd()
// This function does addition of two points.
// return type: BOOL
// FALSE failure in operation; treat as result being point at infinity
LIB_EXPORT BOOL
BnEccAdd(
bigPoint R, // OUT: computed point
pointConst S, // IN: point to multiply by 'd'
pointConst Q, // IN: second point
bigCurve E // IN: curve
)
{
EC_POINT *pR = EC_POINT_new(E->G);
EC_POINT *pS = EcPointInitialized(S, E);
EC_POINT *pQ = EcPointInitialized(Q, E);
//
EC_POINT_add(E->G, pR, pS, pQ, E->CTX);
PointFromOssl(R, pR, E);
EC_POINT_free(pR);
EC_POINT_free(pS);
EC_POINT_free(pQ);
return !BnEqualZero(R->z);
}
#endif // TPM_ALG_ECC
#endif // MATHLIB OSSL