Kawe Mazidjatari b3a68ed095 Add EABase, EAThread and DirtySDK to R5sdk
DirtySDK (EA's Dirty Sockets library) will be used for the LiveAPI implementation, and depends on: EABase, EAThread.
2024-04-05 18:29:03 +02:00

649 lines
22 KiB
C

/*H********************************************************************************/
/*!
\File cryptmont.h
\Description
This module implements the math for elliptic curve cryptography
using montgomery curves
\Copyright
Copyright (c) Electronic Arts 2018. ALL RIGHTS RESERVED.
*/
/********************************************************************************H*/
#include "DirtySDK/dirtysock/dirtylib.h"
#include "DirtySDK/dirtysock/dirtymem.h"
#include "DirtySDK/crypt/cryptrand.h"
#include "DirtySDK/crypt/cryptmont.h"
/*** Defines **********************************************************************/
//! size of the window used to determined the table size
#define CRYPTMONT_WINDOW_SIZE (5)
//! calculation of the table size based on the window
#define CRYPTMONT_TABLE_SIZE (1 << (CRYPTMONT_WINDOW_SIZE - 1))
//! memgroup for allocating the table
#define CRYPTMONT_MEMID ('mont')
//! number of iterations to do per call
#define CRYPTMONT_NUM_ITERATIONS (0x10)
/*** Variables ********************************************************************/
//! prime for x25519
static const uint8_t _aPrime25519[] =
{
0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xed
};
//! prime for x25519
static const uint8_t _aPrime448[] =
{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
/*** Private Functions ************************************************************/
/*F********************************************************************************/
/*!
\Function _CryptMontSwap
\Description
Constant time conditional swap of two big numbers
\Input uSwap - determintes if we should swap
\Input *pLhs - [out] first big number in the swap
\Input *pRhs - [out] second big number in the swap
\Version 04/11/2018 (eesponda)
*/
/********************************************************************************F*/
static void _CryptMontSwap(uint8_t uSwap, CryptBnT *pLhs, CryptBnT *pRhs)
{
CryptBnT Temp, Mask;
// mask = (1 << bits) - swap
CryptBnInitSet(&Mask, 1);
CryptBnInitSet(&Temp, uSwap);
CryptBnLeftShift2(&Mask, DS_MAX(CryptBnBitLen(pLhs), CryptBnBitLen(pRhs)));
CryptBnSubtract(&Mask, &Mask, &Temp);
// temp = mask & (lhs ^ rhs)
CryptBnBitXor(&Temp, pLhs, pRhs);
CryptBnBitAnd(&Temp, &Temp, &Mask);
// lhs ^= temp
CryptBnBitXor(pLhs, pLhs, &Temp);
// rhs ^= temp
CryptBnBitXor(pRhs, pRhs, &Temp);
}
/*F********************************************************************************/
/*!
\Function _CryptMontPointCalculate
\Description
Calculates the point multiplication on the curve based on the private key
before doing the final result calculation
\Input *pState - curve state
\Input *pU - the point we multiply with the private key
\Output
uint8_t - TRUE=complete, FALSE=pending
\Version 04/11/2018 (eesponda)
*/
/********************************************************************************F*/
static uint8_t _CryptMontPointCalculate(CryptMontT *pState, CryptBnT *pU)
{
int32_t iIter;
// initialize the operation
if (pState->iBitIndex < 0)
{
pState->iBitIndex = CryptBnBitLen(&pState->Prime) - 1;
pState->uCryptUsecs = 0;
CryptBnInitSet(&pState->X_2, 1);
CryptBnInitSet(&pState->Result.X, 0);
CryptBnClone(&pState->X_3, pU);
CryptBnInitSet(&pState->Result.Y, 1);
}
for (iIter = 0; (pState->iBitIndex >= 0) && (iIter < CRYPTMONT_NUM_ITERATIONS); pState->iBitIndex -= 1, iIter += 1)
{
CryptBnT A, AA, B, BB, C, D, DA, CB, E;
uint8_t bBitSet = CryptBnBitTest(&pState->PrivateKey, pState->iBitIndex);
const uint64_t uTickUsecs = NetTickUsec();
CryptBnInitSet(&A, 0);
CryptBnInitSet(&B, 0);
CryptBnInitSet(&C, 0);
CryptBnInitSet(&D, 0);
CryptBnInitSet(&E, 0);
// swap ^= bitset;
pState->uSwap ^= bBitSet;
// constant time conditional swap
_CryptMontSwap(pState->uSwap, &pState->X_2, &pState->X_3);
_CryptMontSwap(pState->uSwap, &pState->Result.X, &pState->Result.Y);
// swap = bitset
pState->uSwap = bBitSet;
// A = (x_2 + z_2) % p
CryptBnModAdd(&A, &pState->X_2, &pState->Result.X, &pState->Prime);
// AA = (A * A) % p
CryptBnModMultiply(&AA, &A, &A, &pState->Prime);
// B = (x_2 - z_2)
CryptBnSubtract(&B, &pState->X_2, &pState->Result.X);
// BB = (B * B) % p
CryptBnModMultiply(&BB, &B, &B, &pState->Prime);
// E = (AA - BB)
CryptBnSubtract(&E, &AA, &BB);
// x_2 = (AA * BB) % p
CryptBnModMultiply(&pState->X_2, &AA, &BB, &pState->Prime);
// z_2 = (E * (AA + (a24 * E))) % p
CryptBnModMultiply(&pState->Result.X, &pState->A24, &E, &pState->Prime);
CryptBnModAdd(&pState->Result.X, &AA, &pState->Result.X, &pState->Prime);
CryptBnModMultiply(&pState->Result.X, &E, &pState->Result.X, &pState->Prime);
// C = (x_3 + z_3) % p
CryptBnModAdd(&C, &pState->X_3, &pState->Result.Y, &pState->Prime);
// D = x_3 - z_3
CryptBnSubtract(&D, &pState->X_3, &pState->Result.Y);
// DA = (D * A) % p
CryptBnModMultiply(&DA, &D, &A, &pState->Prime);
// CB = (C * B) % p
CryptBnModMultiply(&CB, &C, &B, &pState->Prime);
// x_3 = (DA+CB)^2 % p
CryptBnModAdd(&pState->X_3, &DA, &CB, &pState->Prime);
CryptBnModMultiply(&pState->X_3, &pState->X_3, &pState->X_3, &pState->Prime);
// z_3 = (u * ((DA-CB)^2 % p)) % p
CryptBnSubtract(&pState->Result.Y, &DA, &CB);
CryptBnModMultiply(&pState->Result.Y, &pState->Result.Y, &pState->Result.Y, &pState->Prime);
CryptBnModMultiply(&pState->Result.Y, pU, &pState->Result.Y, &pState->Prime);
// update timing
pState->uCryptUsecs += (uint32_t)NetTickDiff(NetTickUsec(), uTickUsecs);
}
// check for completion of first stage, perform required operations and move to next
if (pState->iBitIndex < 0)
{
// constant time conditional swap
_CryptMontSwap(pState->uSwap, &pState->X_2, &pState->X_3);
_CryptMontSwap(pState->uSwap, &pState->Result.X, &pState->Result.Y);
}
return((pState->iBitIndex < 0) ? TRUE : FALSE);
}
/*F********************************************************************************/
/*!
\Function _CryptMontResultCalculate
\Description
Determines the final using the final result using the formula:
result = x_2 * (z_2 ^ (p - 2) % p) % p
\Input *pState - curve state
\Output
uint8_t - TRUE=complete, FALSE=pending
\Version 04/11/2018 (eesponda)
*/
/********************************************************************************F*/
static uint8_t _CryptMontResultCalculate(CryptMontT *pState)
{
int32_t iIter;
// initialize the operation
if (pState->iBitIndex < 0)
{
int32_t iTableIndex;
CryptBnT Square;
// query memgroup
DirtyMemGroupQuery(&pState->iMemGroup, &pState->pMemGroupUserdata);
if ((pState->pTable = (CryptBnT *)DirtyMemAlloc(sizeof(*pState->pTable) * CRYPTMONT_TABLE_SIZE, CRYPTMONT_MEMID, pState->iMemGroup, pState->pMemGroupUserdata)) == NULL)
{
NetPrintf(("cryptmont: failed to allocate window table\n"));
return(TRUE);
}
ds_memclr(pState->pTable, sizeof(*pState->pTable) * CRYPTMONT_TABLE_SIZE);
/* put the already reduced input into the first index of the table, this will make it so further
entries in the table will not be too large for our modulus operations */
CryptBnClone(&pState->pTable[0], &pState->Result.X);
// calculate (input^2) % mod as the basis for the other calculations
CryptBnModMultiply(&Square, &pState->pTable[0], &pState->pTable[0], &pState->Prime);
// calculate (input^(2+iTableIndex)) % mod for the rest of the table
for (iTableIndex = 1; iTableIndex < CRYPTMONT_TABLE_SIZE; iTableIndex += 1)
{
CryptBnModMultiply(&pState->pTable[iTableIndex], &pState->pTable[iTableIndex - 1], &Square, &pState->Prime);
}
pState->bAccumulOne = TRUE;
// save prime - 2
CryptBnInitSet(&pState->PrimeMin2, 2);
CryptBnSubtract(&pState->PrimeMin2, &pState->Prime, &pState->PrimeMin2);
pState->iBitIndex = CryptBnBitLen(&pState->PrimeMin2) - 1;
}
for (iIter = 0; (pState->iBitIndex >= 0) && (iIter < CRYPTMONT_NUM_ITERATIONS); iIter += 1)
{
const uint64_t uTickUsecs = NetTickUsec();
/* scan backwards from the current exponent bit until a set bit is found to denote the start of the window
squaring the results until such a bit is found */
if (CryptBnBitTest(&pState->PrimeMin2, pState->iBitIndex))
{
int32_t iWindowBit, iWindowValue, iWindowEnd;
/* scan backwards from the start of the window until the last set bit in the range of the window size is found which denotes the end bit index of the window
calculate the value of the window that will be used when multiplying against our precomputed table we skip the first bit as we know it is set based on the
current exponent bit check we make right above */
for (iWindowBit = 1, iWindowValue = 1, iWindowEnd = 0; (iWindowBit < CRYPTMONT_WINDOW_SIZE) && ((pState->iBitIndex - iWindowBit) >= 0); iWindowBit += 1)
{
if (CryptBnBitTest(&pState->PrimeMin2, pState->iBitIndex - iWindowBit))
{
iWindowValue <<= (iWindowBit - iWindowEnd);
iWindowValue |= 1; /* force odd */
iWindowEnd = iWindowBit;
}
}
// square for all the bits in the window and moving the exponent bit index down each time
for (iWindowBit = 0; iWindowBit < iWindowEnd + 1; iWindowBit += 1, pState->iBitIndex -= 1)
{
CryptBnModMultiply(&pState->Result.X, &pState->Result.X, &pState->Result.X, &pState->Prime);
}
// skip the first multiply
if (!pState->bAccumulOne)
{
CryptBnModMultiply(&pState->Result.X, &pState->Result.X, &pState->pTable[iWindowValue/2], &pState->Prime);
}
else
{
CryptBnClone(&pState->Result.X, &pState->pTable[iWindowValue/2]);
pState->bAccumulOne = FALSE;
}
}
else
{
CryptBnModMultiply(&pState->Result.X, &pState->Result.X, &pState->Result.X, &pState->Prime);
pState->iBitIndex -= 1;
}
// update timing
pState->uCryptUsecs += (uint32_t)NetTickDiff(NetTickUsec(), uTickUsecs);
}
// calculate the final result
if (pState->iBitIndex < 0)
{
DirtyMemFree(pState->pTable, CRYPTMONT_MEMID, pState->iMemGroup, pState->pMemGroupUserdata);
pState->pTable = NULL;
CryptBnModMultiply(&pState->Result.X, &pState->X_2, &pState->Result.X, &pState->Prime);
}
return((pState->iBitIndex < 0) ? TRUE : FALSE);
}
/*F********************************************************************************/
/*!
\Function _CryptMontInit25519PrivateKey
\Description
Initializes the private key based on the requirements for this curve
(x25519)
\Input *pPrivateKey - [out] private key state
\Input *pK - private key buffer or NULL if we should generate one
\Version 04/11/2018 (eesponda)
*/
/********************************************************************************F*/
static void _CryptMontInit25519PrivateKey(CryptBnT *pPrivateKey, const uint8_t *pK)
{
uint8_t aSecret[32];
/* per: https://tools.ietf.org/html/rfc7748#section-5
For X25519, in order to decode 32 random bytes as an integer scalar, set the three least significant bits of the first byte and
the most significant bit of the last to zero, set the second most significant bit of the last byte to 1 and, finally, decode as
little-endian. This means that the resulting integer is of the form 2^254 plus eight times a value between 0 and 2^251 - 1 (inclusive) */
// retrieve the random bytes if not provided one
if (pK == NULL)
{
CryptRandGet(aSecret, sizeof(aSecret));
}
else
{
ds_memcpy(aSecret, pK, sizeof(aSecret));
}
aSecret[0] &= 248;
aSecret[31] &= 127;
aSecret[31] |= 64;
CryptBnInitLeFrom(pPrivateKey, aSecret, sizeof(aSecret));
}
/*F********************************************************************************/
/*!
\Function _CryptMontInit448PrivateKey
\Description
Initializes the private key based on the requirements for this curve
(x448)
\Input *pPrivateKey - [out] private key state
\Input *pK - private key buffer or NULL if we should generate one
\Version 04/11/2018 (eesponda)
*/
/********************************************************************************F*/
static void _CryptMontInit448PrivateKey(CryptBnT *pPrivateKey, const uint8_t *pK)
{
uint8_t aSecret[56];
/* per: https://tools.ietf.org/html/rfc7748#section-5
Likewise, for X448, set the two least significant bits of the first byte to 0, and the most significant bit of the last byte to 1. This
means that the resulting integer is of the form 2^447 plus four times a value between 0 and 2^445 - 1 (inclusive). */
if (pK == NULL)
{
CryptRandGet(aSecret, sizeof(aSecret));
}
else
{
ds_memcpy(aSecret, pK, sizeof(aSecret));
}
aSecret[0] &= 252;
aSecret[55] |= 128;
CryptBnInitLeFrom(pPrivateKey, aSecret, sizeof(aSecret));
}
/*** Public Functions ************************************************************/
/*F********************************************************************************/
/*!
\Function CryptMontInit
\Description
Initializes the curve given an identifier
\Input *pState - curve state we are initializing
\Input iCurveType - the curve identifier (CRYPTMONT_CURVE_*)
\Output
int32_t - 0=success, negative=failure
\Version 04/11/2018 (eesponda)
*/
/********************************************************************************F*/
int32_t CryptMontInit(CryptMontT *pState, int32_t iCurveType)
{
// init state
ds_memclr(pState, sizeof(*pState));
pState->iBitIndex = -1;
pState->iCurveType = iCurveType;
if (iCurveType == CRYPTCURVE_X25519)
{
// save prime
CryptBnInitFrom(&pState->Prime, -1, _aPrime25519, sizeof(_aPrime25519));
// save a24
CryptBnInitSet(&pState->A24, 121665);
// save u
CryptBnInitSet(&pState->BasePoint, 9);
// get k
_CryptMontInit25519PrivateKey(&pState->PrivateKey, NULL);
}
else if (iCurveType == CRYPTCURVE_X448)
{
// save prime
CryptBnInitFrom(&pState->Prime, -1, _aPrime448, sizeof(_aPrime448));
// save a24
CryptBnInitSet(&pState->A24, 39081);
// save u
CryptBnInitSet(&pState->BasePoint, 5);
// get k
_CryptMontInit448PrivateKey(&pState->PrivateKey, NULL);
}
else
{
NetPrintf(("cryptmont: unrecognized curve type (%d) passed to init\n", iCurveType));
return(-1);
}
return(0);
}
/*F********************************************************************************/
/*!
\Function CryptMontSetPrivateKey
\Description
Sets our internal private key for verifying our test vectors based
on the state's curve type
\Input *pState - curve state
\Input *pKey - the private key buffer
\Notes
This is for testing purposes only
\Version 04/11/2018 (eesponda)
*/
/********************************************************************************F*/
void CryptMontSetPrivateKey(CryptMontT *pState, const uint8_t *pKey)
{
#if DIRTYCODE_DEBUG
if (pState->iCurveType == CRYPTCURVE_X25519)
{
_CryptMontInit25519PrivateKey(&pState->PrivateKey, pKey);
}
else if (pState->iCurveType == CRYPTCURVE_X448)
{
_CryptMontInit448PrivateKey(&pState->PrivateKey, pKey);
}
#endif
}
/*F********************************************************************************/
/*!
\Function CryptMontPublic
\Description
Generates our public key by doing our PrivateKey * BasePoint on the curve
\Input *pState - curve state
\Input *pResult - [out] result output (optional)
\Input *pCryptUsecs - [out] timing information for the operation (optional)
\Output
int32_t - zero=success, otherwise=pending
\Notes
If pResult is NULL, the result can be pulled from CryptMontT.Result
\Version 04/11/2018 (eesponda)
*/
/********************************************************************************F*/
int32_t CryptMontPublic(CryptMontT *pState, CryptEccPointT *pResult, uint32_t *pCryptUsecs)
{
int32_t iResult = 1;
if ((pState->eState == CRYPTMONT_COMPUTE_POINT) && (_CryptMontPointCalculate(pState, &pState->BasePoint)))
{
// switch state on completion
pState->eState = CRYPTMONT_COMPUTE_EXP;
}
else if ((pState->eState == CRYPTMONT_COMPUTE_EXP) && (_CryptMontResultCalculate(pState)))
{
// reset back to original state in case they want to perform a different operation
pState->eState = CRYPTMONT_COMPUTE_POINT;
// copy the timing if passed in
if (pCryptUsecs != NULL)
{
*pCryptUsecs = pState->uCryptUsecs;
}
// signal completion
if (pResult != NULL)
{
CryptBnClone(&pResult->X, &pState->Result.X);
}
iResult = 0;
}
return(iResult);
}
/*F********************************************************************************/
/*!
\Function CryptMontSecret
\Description
Generates our shared secret by doing our PrivateKey * PublicKey on the curve
\Input *pState - curve state
\Input *pPublicKey - the peer's public key
\Input *pResult - [out] result output (optional)
\Input *pCryptUsecs - [out] timing information for the operation (optional)
\Output
int32_t - zero=success, otherwise=pending
\Notes
If pResult is NULL, the result can be pulled from CryptMontT.Result
\Version 04/11/2018 (eesponda)
*/
/********************************************************************************F*/
int32_t CryptMontSecret(CryptMontT *pState, CryptEccPointT *pPublicKey, CryptEccPointT *pResult, uint32_t *pCryptUsecs)
{
int32_t iResult = 1;
if ((pState->eState == CRYPTMONT_COMPUTE_POINT) && (_CryptMontPointCalculate(pState, &pPublicKey->X)))
{
// switch state on completion
pState->eState = CRYPTMONT_COMPUTE_EXP;
}
else if ((pState->eState == CRYPTMONT_COMPUTE_EXP) && (_CryptMontResultCalculate(pState)))
{
// reset back to original state in case they want to perform a different operation
pState->eState = CRYPTMONT_COMPUTE_POINT;
// copy the timing if passed in
if (pCryptUsecs != NULL)
{
*pCryptUsecs = pState->uCryptUsecs;
}
// signal completion
if (pResult != NULL)
{
CryptBnClone(&pResult->X, &pState->Result.X);
}
iResult = 0;
}
return(iResult);
}
/*F********************************************************************************/
/*!
\Function CryptMontPointInitFrom
\Description
Initializes our point representation given a buffer
\Input *pPoint - point state
\Input *pBuffer - the buffer we are copying from
\Input iBufSize - size of the buffer
\Output
int32_t - zero=success, otherwise=failure
\Notes
These curves work in little endian so need to copy different functions
from our nist curves.
\Version 04/11/2018 (eesponda)
*/
/********************************************************************************F*/
int32_t CryptMontPointInitFrom(CryptEccPointT *pPoint, const uint8_t *pBuffer, int32_t iBufSize)
{
CryptBnInitLeFrom(&pPoint->X, pBuffer, iBufSize);
return(0);
}
/*F********************************************************************************/
/*!
\Function CryptMontPointFinal
\Description
Copies our point data into an output buffer
\Input *pState - curve state
\Input *pPoint - point state or NULL to use curve result
\Input bSecret - is this the shared secret?
\Input *pBuffer - [out] the buffer we are copying into
\Input iBufSize - size of the buffer
\Output
int32_t - number of bytes encoded into the buffer
\Notes
These curves work in little endian so need to copy different functions
from our nist curves.
The bSecret is unused here but is left for compatibility with the other
curves' API.
\Version 04/11/2018 (eesponda)
*/
/********************************************************************************F*/
int32_t CryptMontPointFinal(const CryptMontT *pState, const CryptEccPointT *pPoint, uint8_t bSecret, uint8_t *pBuffer, int32_t iBufSize)
{
// if point not provided assume we are using the result
if ((pState != NULL) && (pPoint == NULL))
{
pPoint = &pState->Result;
}
CryptBnFinalLe(&pPoint->X, pBuffer, iBufSize);
return(CryptBnByteLen(&pPoint->X));
}