blob: 5f8bd3450cd6e44e9a41220c418190df56722415 [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
// The functions in this file provide the low-level interface between the TPM code
// and the big number and elliptic curve math routines in OpenSSL.
//
// Most math on big numbers require a context. The context contains the memory in
// which OpenSSL creates and manages the big number values. When a OpenSSL math
// function will be called that modifies a BIGNUM value, that value must be created in
// an OpenSSL context. The first line of code in such a function must be:
// OSSL_ENTER(); and the last operation before returning must be OSSL_LEAVE().
// OpenSSL variables can then be created with BnNewVariable(). Constant values to be
// used by OpenSSL are created from the bigNum values passed to the functions in this
// file. Space for the BIGNUM control block is allocated in the stack of the
// function and then it is initialized by calling BigInitialized(). That function
// sets up the values in the BIGNUM structure and sets the data pointer to point to
// the data in the bignum_t. This is only used when the value is known to be a
// constant in the called function.
//
// Because the allocations of constants is on the local stack and the
// OSSL_ENTER()/OSSL_LEAVE() pair flushes everything created in OpenSSL memory, there
// should be no chance of a memory leak.
//** Includes and Defines
#include "Tpm.h"
#ifdef 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 uses a different control structure but the same data
// layout -- an array of native-endian words in little-endian order.
// Return Type: BOOL
// TRUE(1) success
// FALSE(0) failure because value will not fit or OpenSSL variable doesn't
// exist
BOOL
OsslToTpmBn(
bigNum bn,
BIGNUM *osslBn
)
{
VERIFY(osslBn != NULL);
// If the bn is NULL, it means that an output value pointer was NULL meaning that
// the results is simply to be discarded.
if(bn != NULL)
{
int i;
//
VERIFY((unsigned)osslBn->width <= BnGetAllocated(bn));
for(i = 0; i < osslBn->width; i++)
bn->d[i] = osslBn->d[i];
BnSetTop(bn, osslBn->width);
}
return TRUE;
Error:
return FALSE;
}
//*** BigInitialized()
// This function initializes an OSSL BIGNUM from a TPM bigConst. Do not use this for
// values that are passed to OpenSLL when they are not declared as const in the
// function prototype. Instead, use BnNewVariable().
BIGNUM *
BigInitialized(
BIGNUM *toInit,
bigConst initializer
)
{
if(initializer == NULL)
FAIL(FATAL_ERROR_PARAMETER);
if(toInit == NULL || initializer == NULL)
return NULL;
toInit->d = (BN_ULONG *)&initializer->d[0];
toInit->dmax = (int)initializer->allocated;
toInit->width = (int)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))
//*** BIGNUM_print()
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
//*** BnNewVariable()
// This function allocates a new variable in the provided context. If the context
// does not exist or the allocation fails, it is a catastrophic failure.
static BIGNUM *
BnNewVariable(
BN_CTX *CTX
)
{
BIGNUM *new;
//
// This check is intended to protect against calling this function without
// having initialized the CTX.
if((CTX == NULL) || ((new = BN_CTX_get(CTX)) == NULL))
FAIL(FATAL_ERROR_ALLOCATION);
return new;
}
#if LIBRARY_COMPATIBILITY_CHECK
//*** MathLibraryCompatibilityCheck()
BOOL
MathLibraryCompatibilityCheck(
void
)
{
OSSL_ENTER();
BIGNUM *osslTemp = BnNewVariable(CTX);
crypt_uword_t i;
BYTE test[] = {0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18,
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00};
BN_VAR(tpmTemp, sizeof(test) * 8); // allocate some space for a test value
//
// Convert the test data to a bigNum
BnFromBytes(tpmTemp, test, sizeof(test));
// Convert the test data to an OpenSSL BIGNUM
BN_bin2bn(test, sizeof(test), osslTemp);
// Make sure the values are consistent
VERIFY(osslTemp->width == (int)tpmTemp->size);
for(i = 0; i < tpmTemp->size; i++)
VERIFY(osslTemp->d[i] == tpmTemp->d[i]);
OSSL_LEAVE();
return 1;
Error:
return 0;
}
#endif
//*** BnModMult()
// This function does a modular multiply. It first does a multiply and then a divide
// and returns the remainder of the divide.
// Return Type: BOOL
// TRUE(1) success
// FALSE(0) failure in operation
LIB_EXPORT BOOL
BnModMult(
bigNum result,
bigConst op1,
bigConst op2,
bigConst modulus
)
{
OSSL_ENTER();
BOOL OK = TRUE;
BIGNUM *bnResult = BN_NEW();
BIGNUM *bnTemp = BN_NEW();
BIG_INITIALIZED(bnOp1, op1);
BIG_INITIALIZED(bnOp2, op2);
BIG_INITIALIZED(bnMod, modulus);
//
VERIFY(BN_mul(bnTemp, bnOp1, bnOp2, CTX));
VERIFY(BN_div(NULL, bnResult, bnTemp, bnMod, CTX));
VERIFY(OsslToTpmBn(result, bnResult));
goto Exit;
Error:
OK = FALSE;
Exit:
OSSL_LEAVE();
return OK;
}
//*** BnMult()
// Multiplies two numbers
// Return Type: BOOL
// TRUE(1) success
// FALSE(0) failure in operation
LIB_EXPORT BOOL
BnMult(
bigNum result,
bigConst multiplicand,
bigConst multiplier
)
{
OSSL_ENTER();
BIGNUM *bnTemp = BN_NEW();
BOOL OK = TRUE;
BIG_INITIALIZED(bnA, multiplicand);
BIG_INITIALIZED(bnB, multiplier);
//
VERIFY(BN_mul(bnTemp, bnA, bnB, CTX));
VERIFY(OsslToTpmBn(result, bnTemp));
goto Exit;
Error:
OK = FALSE;
Exit:
OSSL_LEAVE();
return OK;
}
//*** BnDiv()
// This function divides two bigNum values. The function returns FALSE if
// there is an error in the operation.
// Return Type: BOOL
// TRUE(1) success
// FALSE(0) failure in operation
LIB_EXPORT BOOL
BnDiv(
bigNum quotient,
bigNum remainder,
bigConst dividend,
bigConst divisor
)
{
OSSL_ENTER();
BIGNUM *bnQ = BN_NEW();
BIGNUM *bnR = BN_NEW();
BOOL OK = TRUE;
BIG_INITIALIZED(bnDend, dividend);
BIG_INITIALIZED(bnSor, divisor);
//
if(BnEqualZero(divisor))
FAIL(FATAL_ERROR_DIVIDE_ZERO);
VERIFY(BN_div(bnQ, bnR, bnDend, bnSor, CTX));
VERIFY(OsslToTpmBn(quotient, bnQ));
VERIFY(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);
goto Exit;
Error:
OK = FALSE;
Exit:
OSSL_LEAVE();
return OK;
}
#if ALG_RSA
//*** BnGcd()
// Get the greatest common divisor of two numbers
// Return Type: BOOL
// TRUE(1) success
// FALSE(0) failure in operation
LIB_EXPORT BOOL
BnGcd(
bigNum gcd, // OUT: the common divisor
bigConst number1, // IN:
bigConst number2 // IN:
)
{
OSSL_ENTER();
BIGNUM *bnGcd = BN_NEW();
BOOL OK = TRUE;
BIG_INITIALIZED(bn1, number1);
BIG_INITIALIZED(bn2, number2);
//
VERIFY(BN_gcd(bnGcd, bn1, bn2, CTX));
VERIFY(OsslToTpmBn(gcd, bnGcd));
goto Exit;
Error:
OK = FALSE;
Exit:
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
// Return Type: BOOL
// TRUE(1) success
// FALSE(0) failure in operation
LIB_EXPORT BOOL
BnModExp(
bigNum result, // OUT: the result
bigConst number, // IN: number to exponentiate
bigConst exponent, // IN:
bigConst modulus // IN:
)
{
OSSL_ENTER();
BIGNUM *bnResult = BN_NEW();
BOOL OK = TRUE;
BIG_INITIALIZED(bnN, number);
BIG_INITIALIZED(bnE, exponent);
BIG_INITIALIZED(bnM, modulus);
//
VERIFY(BN_mod_exp(bnResult, bnN, bnE, bnM, CTX));
VERIFY(OsslToTpmBn(result, bnResult));
goto Exit;
Error:
OK = FALSE;
Exit:
OSSL_LEAVE();
return OK;
}
//*** BnModInverse()
// Modular multiplicative inverse
// Return Type: BOOL
// TRUE(1) success
// FALSE(0) failure in operation
LIB_EXPORT BOOL
BnModInverse(
bigNum result,
bigConst number,
bigConst modulus
)
{
OSSL_ENTER();
BIGNUM *bnResult = BN_NEW();
BOOL OK = TRUE;
BIG_INITIALIZED(bnN, number);
BIG_INITIALIZED(bnM, modulus);
//
VERIFY(BN_mod_inverse(bnResult, bnN, bnM, CTX) != NULL);
VERIFY(OsslToTpmBn(result, bnResult));
goto Exit;
Error:
OK = FALSE;
Exit:
OSSL_LEAVE();
return OK;
}
#endif // ALG_RSA
#if ALG_ECC
//*** PointFromOssl()
// Function to copy the point result from an OSSL function to a bigNum
// Return Type: BOOL
// TRUE(1) success
// FALSE(0) failure in operation
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
)
{
EC_POINT *P = NULL;
if(initializer != NULL)
{
BIG_INITIALIZED(bnX, initializer->x);
BIG_INITIALIZED(bnY, initializer->y);
if(E == NULL)
FAIL(FATAL_ERROR_ALLOCATION);
P = EC_POINT_new(E->G);
if(!EC_POINT_set_affine_coordinates_GFp(E->G, P, bnX, bnY, E->CTX))
P = NULL;
}
return P;
}
//*** BnCurveInitialize()
// This function initializes the OpenSSL curve information structure. This
// structure points to the TPM-defined values for the curve, to the context for the
// number values in the frame, and to the OpenSSL-defined group values.
// Return Type: bigCurve *
// NULL the TPM_ECC_CURVE is not valid or there was a problem in
// in initializing the curve data
// non-NULL points to 'E'
LIB_EXPORT bigCurve
BnCurveInitialize(
bigCurve E, // IN: curve structure to initialize
TPM_ECC_CURVE curveId // IN: curve identifier
)
{
const ECC_CURVE_DATA *C = GetCurveData(curveId);
if(C == NULL)
E = NULL;
if(E != NULL)
{
// This creates the OpenSSL memory context that stays in effect as long as the
// curve (E) is defined.
OSSL_ENTER(); // if the allocation fails, the TPM fails
EC_POINT *P = NULL;
BIG_INITIALIZED(bnP, C->prime);
BIG_INITIALIZED(bnA, C->a);
BIG_INITIALIZED(bnB, C->b);
BIG_INITIALIZED(bnX, C->base.x);
BIG_INITIALIZED(bnY, C->base.y);
BIG_INITIALIZED(bnN, C->order);
BIG_INITIALIZED(bnH, C->h);
//
E->C = C;
E->CTX = CTX;
// initialize EC group, associate a generator point and initialize the point
// from the parameter data
// Create a group structure
E->G = EC_GROUP_new_curve_GFp(bnP, bnA, bnB, CTX);
VERIFY(E->G != NULL);
// Allocate a point in the group that will be used in setting the
// generator. This is not needed after the generator is set.
P = EC_POINT_new(E->G);
VERIFY(P != NULL);
// Need to use this in case Montgomery method is being used
VERIFY(EC_POINT_set_affine_coordinates_GFp(E->G, P, bnX, bnY, CTX));
// Now set the generator
VERIFY(EC_GROUP_set_generator(E->G, P, bnN, bnH));
EC_POINT_free(P);
goto Exit;
Error:
EC_POINT_free(P);
BnCurveFree(E);
E = NULL;
}
Exit:
return E;
}
//*** BnCurveFree()
// This function will free the allocated components of the curve and end the
// frame in which the curve data exists
LIB_EXPORT void
BnCurveFree(
bigCurve E
)
{
if(E)
{
EC_GROUP_free(E->G);
OsslContextLeave(E->CTX);
}
}
//*** BnEccModMult()
// This function does a point multiply of the form R = [d]S
// Return Type: BOOL
// TRUE(1) success
// FALSE(0) 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
// TRUE(1) success
// FALSE(0) 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);
BIG_INITIALIZED(bnD, d);
EC_POINT *pQ = EcPointInitialized(Q, E);
BIG_INITIALIZED(bnU, u);
if(S == NULL || S == (pointConst)&(AccessCurveData(E)->base))
EC_POINT_mul(E->G, pR, bnD, pQ, bnU, E->CTX);
else
{
return FALSE;
}
PointFromOssl(R, pR, E);
EC_POINT_free(pR);
EC_POINT_free(pQ);
return !BnEqualZero(R->z);
}
//** BnEccAdd()
// This function does addition of two points.
// Return Type: BOOL
// TRUE(1) success
// FALSE(0) 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 // ALG_ECC
#endif // MATHLIB OSSL