summaryrefslogtreecommitdiffstats
path: root/src/tpm2/crypto/openssl/CryptEccSignature.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/tpm2/crypto/openssl/CryptEccSignature.c1043
1 files changed, 1043 insertions, 0 deletions
diff --git a/src/tpm2/crypto/openssl/CryptEccSignature.c b/src/tpm2/crypto/openssl/CryptEccSignature.c
new file mode 100644
index 0000000..a325a46
--- /dev/null
+++ b/src/tpm2/crypto/openssl/CryptEccSignature.c
@@ -0,0 +1,1043 @@
+/********************************************************************************/
+/* */
+/* ECC Signatures */
+/* Written by Ken Goldman */
+/* IBM Thomas J. Watson Research Center */
+/* $Id: CryptEccSignature.c 1658 2021-01-22 23:14:01Z kgoldman $ */
+/* */
+/* Licenses and Notices */
+/* */
+/* 1. Copyright Licenses: */
+/* */
+/* - Trusted Computing Group (TCG) grants to the user of the source code in */
+/* this specification (the "Source Code") a worldwide, irrevocable, */
+/* nonexclusive, royalty free, copyright license to reproduce, create */
+/* derivative works, distribute, display and perform the Source Code and */
+/* derivative works thereof, and to grant others the rights granted herein. */
+/* */
+/* - The TCG grants to the user of the other parts of the specification */
+/* (other than the Source Code) the rights to reproduce, distribute, */
+/* display, and perform the specification solely for the purpose of */
+/* developing products based on such documents. */
+/* */
+/* 2. Source Code Distribution Conditions: */
+/* */
+/* - Redistributions of Source Code must retain the above copyright licenses, */
+/* this list of conditions and the following disclaimers. */
+/* */
+/* - Redistributions in binary form must reproduce the above copyright */
+/* licenses, this list of conditions and the following disclaimers in the */
+/* documentation and/or other materials provided with the distribution. */
+/* */
+/* 3. Disclaimers: */
+/* */
+/* - THE COPYRIGHT LICENSES SET FORTH ABOVE DO NOT REPRESENT ANY FORM OF */
+/* LICENSE OR WAIVER, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, WITH */
+/* RESPECT TO PATENT RIGHTS HELD BY TCG MEMBERS (OR OTHER THIRD PARTIES) */
+/* THAT MAY BE NECESSARY TO IMPLEMENT THIS SPECIFICATION OR OTHERWISE. */
+/* Contact TCG Administration (admin@trustedcomputinggroup.org) for */
+/* information on specification licensing rights available through TCG */
+/* membership agreements. */
+/* */
+/* - THIS SPECIFICATION IS PROVIDED "AS IS" WITH NO EXPRESS OR IMPLIED */
+/* WARRANTIES WHATSOEVER, INCLUDING ANY WARRANTY OF MERCHANTABILITY OR */
+/* FITNESS FOR A PARTICULAR PURPOSE, ACCURACY, COMPLETENESS, OR */
+/* NONINFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS, OR ANY WARRANTY */
+/* OTHERWISE ARISING OUT OF ANY PROPOSAL, SPECIFICATION OR SAMPLE. */
+/* */
+/* - Without limitation, TCG and its members and licensors disclaim all */
+/* liability, including liability for infringement of any proprietary */
+/* rights, relating to use of information in this specification and to the */
+/* implementation of this specification, and TCG disclaims all liability for */
+/* cost of procurement of substitute goods or services, lost profits, loss */
+/* of use, loss of data or any incidental, consequential, direct, indirect, */
+/* or special damages, whether under contract, tort, warranty or otherwise, */
+/* arising in any way out of use or reliance upon this specification or any */
+/* information herein. */
+/* */
+/* (c) Copyright IBM Corp. and others, 2016 - 2021 */
+/* */
+/********************************************************************************/
+
+/* 10.2.12 CryptEccSignature.c */
+/* 10.2.12.1 Includes and Defines */
+#include "Tpm.h"
+#include "CryptEccSignature_fp.h"
+#include "TpmToOsslMath_fp.h" // libtpms added
+#if ALG_ECC
+/* 10.2.12.2 Utility Functions */
+/* 10.2.12.2.1 EcdsaDigest() */
+/* Function to adjust the digest so that it is no larger than the order of the curve. This is used
+ for ECDSA sign and verification. */
+#if !USE_OPENSSL_FUNCTIONS_ECDSA // libtpms added
+static bigNum
+EcdsaDigest(
+ bigNum bnD, // OUT: the adjusted digest
+ const TPM2B_DIGEST *digest, // IN: digest to adjust
+ bigConst max // IN: value that indicates the maximum
+ // number of bits in the results
+ )
+{
+ int bitsInMax = BnSizeInBits(max);
+ int shift;
+ //
+ if(digest == NULL)
+ BnSetWord(bnD, 0);
+ else
+ {
+ BnFromBytes(bnD, digest->t.buffer,
+ (NUMBYTES)MIN(digest->t.size, BITS_TO_BYTES(bitsInMax)));
+ shift = BnSizeInBits(bnD) - bitsInMax;
+ if(shift > 0)
+ BnShiftRight(bnD, bnD, shift);
+ }
+ return bnD;
+}
+#endif // libtpms added
+/* 10.2.12.2.2 BnSchnorrSign() */
+/* This contains the Schnorr signature computation. It is used by both ECDSA and Schnorr
+ signing. The result is computed as: [s = k + r * d (mod n)] where */
+/* a) s is the signature */
+/* b) k is a random value */
+/* c) r is the value to sign */
+/* d) d is the private EC key */
+/* e) n is the order of the curve */
+/* Error Returns Meaning */
+/* TPM_RC_NO_RESULT the result of the operation was zero or r (mod n) is zero */
+static TPM_RC
+BnSchnorrSign(
+ bigNum bnS, // OUT: s component of the signature
+ bigConst bnK, // IN: a random value
+ bigNum bnR, // IN: the signature 'r' value
+ bigConst bnD, // IN: the private key
+ bigConst bnN // IN: the order of the curve
+ )
+{
+ // Need a local temp value to store the intermediate computation because product
+ // size can be larger than will fit in bnS.
+ BN_VAR(bnT1, MAX_ECC_PARAMETER_BYTES * 2 * 8);
+ //
+ // Reduce bnR without changing the input value
+ BnDiv(NULL, bnT1, bnR, bnN);
+ if(BnEqualZero(bnT1))
+ return TPM_RC_NO_RESULT;
+ // compute s = (k + r * d)(mod n)
+ // r * d
+ BnMult(bnT1, bnT1, bnD);
+ // k * r * d
+ BnAdd(bnT1, bnT1, bnK);
+ // k + r * d (mod n)
+ BnDiv(NULL, bnS, bnT1, bnN);
+ return (BnEqualZero(bnS)) ? TPM_RC_NO_RESULT : TPM_RC_SUCCESS;
+}
+/* 10.2.12.3 Signing Functions */
+/* 10.2.12.3.1 BnSignEcdsa() */
+/* This function implements the ECDSA signing algorithm. The method is described in the comments
+ below. This version works with internal numbers. */
+#if !USE_OPENSSL_FUNCTIONS_ECDSA // libtpms added
+TPM_RC
+BnSignEcdsa(
+ bigNum bnR, // OUT: r component of the signature
+ bigNum bnS, // OUT: s component of the signature
+ bigCurve E, // IN: the curve used in the signature
+ // process
+ bigNum bnD, // IN: private signing key
+ const TPM2B_DIGEST *digest, // IN: the digest to sign
+ RAND_STATE *rand // IN: used in debug of signing
+ )
+{
+ ECC_NUM(bnK);
+ ECC_NUM(bnIk);
+ BN_VAR(bnE, MAX(MAX_ECC_KEY_BYTES, MAX_DIGEST_SIZE) * 8);
+ POINT(ecR);
+ bigConst order = CurveGetOrder(AccessCurveData(E));
+ TPM_RC retVal = TPM_RC_SUCCESS;
+ INT32 tries = 10;
+ BOOL OK = FALSE;
+ //
+ pAssert(digest != NULL);
+ // The algorithm as described in "Suite B Implementer's Guide to FIPS
+ // 186-3(ECDSA)"
+ // 1. Use one of the routines in Appendix A.2 to generate (k, k^-1), a
+ // per-message secret number and its inverse modulo n. Since n is prime,
+ // the output will be invalid only if there is a failure in the RBG.
+ // 2. Compute the elliptic curve point R = [k]G = (xR, yR) using EC scalar
+ // multiplication (see [Routines]), where G is the base point included in
+ // the set of domain parameters.
+ // 3. Compute r = xR mod n. If r = 0, then return to Step 1. 1.
+ // 4. Use the selected hash function to compute H = Hash(M).
+ // 5. Convert the bit string H to an integer e as described in Appendix B.2.
+ // 6. Compute s = (k^-1 * (e + d * r)) mod q. If s = 0, return to Step 1.2.
+ // 7. Return (r, s).
+ // In the code below, q is n (that it, the order of the curve is p)
+ do // This implements the loop at step 6. If s is zero, start over.
+ {
+ for(; tries > 0; tries--)
+ {
+ // Step 1 and 2 -- generate an ephemeral key and the modular inverse
+ // of the private key.
+ if(!BnEccGenerateKeyPair(bnK, ecR, E, rand))
+ continue;
+ // x coordinate is mod p. Make it mod q
+ BnMod(ecR->x, order);
+ // Make sure that it is not zero;
+ if(BnEqualZero(ecR->x))
+ continue;
+ // write the modular reduced version of r as part of the signature
+ BnCopy(bnR, ecR->x);
+ // Make sure that a modular inverse exists and try again if not
+ OK = (BnModInverse(bnIk, bnK, order));
+ if(OK)
+ break;
+ }
+ if(!OK)
+ goto Exit;
+ EcdsaDigest(bnE, digest, order);
+ // now have inverse of K (bnIk), e (bnE), r (bnR), d (bnD) and
+ // CurveGetOrder(E)
+ // Compute s = k^-1 (e + r*d)(mod q)
+ // first do s = r*d mod q
+ BnModMult(bnS, bnR, bnD, order);
+ // s = e + s = e + r * d
+ BnAdd(bnS, bnE, bnS);
+ // s = k^(-1)s (mod n) = k^(-1)(e + r * d)(mod n)
+ BnModMult(bnS, bnIk, bnS, order);
+ // If S is zero, try again
+ } while(BnEqualZero(bnS));
+ Exit:
+ return retVal;
+}
+#else // !USE_OPENSSL_FUNCTIONS_ECDSA libtpms added begin
+TPM_RC
+BnSignEcdsa(
+ bigNum bnR, // OUT: r component of the signature
+ bigNum bnS, // OUT: s component of the signature
+ bigCurve E, // IN: the curve used in the signature
+ // process
+ bigNum bnD, // IN: private signing key
+ const TPM2B_DIGEST *digest, // IN: the digest to sign
+ RAND_STATE *rand // IN: used in debug of signing
+ )
+{
+ ECDSA_SIG *sig = NULL;
+ EC_KEY *eckey;
+ int retVal;
+ const BIGNUM *r;
+ const BIGNUM *s;
+ BIGNUM *d = BN_new();
+
+ d = BigInitialized(d, bnD);
+
+ eckey = EC_KEY_new();
+
+ if (d == NULL || eckey == NULL)
+ ERROR_RETURN(TPM_RC_FAILURE);
+
+ if (EC_KEY_set_group(eckey, E->G) != 1)
+ ERROR_RETURN(TPM_RC_FAILURE);
+
+ if (EC_KEY_set_private_key(eckey, d) != 1)
+ ERROR_RETURN(TPM_RC_FAILURE);
+
+ sig = ECDSA_do_sign(digest->b.buffer, digest->b.size, eckey);
+ if (sig == NULL)
+ ERROR_RETURN(TPM_RC_FAILURE);
+
+ ECDSA_SIG_get0(sig, &r, &s);
+ OsslToTpmBn(bnR, r);
+ OsslToTpmBn(bnS, s);
+
+ retVal = TPM_RC_SUCCESS;
+
+ Exit:
+ BN_clear_free(d);
+ EC_KEY_free(eckey);
+ ECDSA_SIG_free(sig);
+
+ return retVal;
+}
+#endif // USE_OPENSSL_FUNCTIONS_ECDSA libtpms added end
+#if ALG_ECDAA
+/* 10.2.12.3.2 BnSignEcdaa() */
+/* This function performs s = r + T * d mod q where */
+/* a) 'r is a random, or pseudo-random value created in the commit phase */
+/* b) nonceK is a TPM-generated, random value 0 < nonceK < n */
+/* c) T is mod q of Hash(nonceK || digest), and */
+/* d) d is a private key. */
+/* The signature is the tuple (nonceK, s) */
+/* Regrettably, the parameters in this function kind of collide with the parameter names used in
+ ECSCHNORR making for a lot of confusion. In particular, the k value in this function is value in
+ this function u */
+/* Error Returns Meaning */
+/* TPM_RC_SCHEME unsupported hash algorithm */
+/* TPM_RC_NO_RESULT cannot get values from random number generator */
+static TPM_RC
+BnSignEcdaa(
+ TPM2B_ECC_PARAMETER *nonceK, // OUT: nonce component of the signature
+ bigNum bnS, // OUT: s component of the signature
+ bigCurve E, // IN: the curve used in signing
+ bigNum bnD, // IN: the private key
+ const TPM2B_DIGEST *digest, // IN: the value to sign (mod q)
+ TPMT_ECC_SCHEME *scheme, // IN: signing scheme (contains the
+ // commit count value).
+ OBJECT *eccKey, // IN: The signing key
+ RAND_STATE *rand // IN: a random number state
+ )
+{
+ TPM_RC retVal;
+ TPM2B_ECC_PARAMETER r;
+ HASH_STATE state;
+ TPM2B_DIGEST T;
+ BN_MAX(bnT);
+ //
+ NOT_REFERENCED(rand);
+ if(!CryptGenerateR(&r, &scheme->details.ecdaa.count,
+ eccKey->publicArea.parameters.eccDetail.curveID,
+ &eccKey->name))
+ retVal = TPM_RC_VALUE;
+ else
+ {
+ // This allocation is here because 'r' doesn't have a value until
+ // CrypGenerateR() is done.
+ ECC_INITIALIZED(bnR, &r);
+ do
+ {
+ // generate nonceK such that 0 < nonceK < n
+ // use bnT as a temp.
+#if USE_OPENSSL_FUNCTIONS_EC // libtpms added begin
+ if(!BnEccGetPrivate(bnT, AccessCurveData(E), E->G, false, rand))
+#else // libtpms added end
+ if(!BnEccGetPrivate(bnT, AccessCurveData(E), rand))
+#endif // libtpms added
+ {
+ retVal = TPM_RC_NO_RESULT;
+ break;
+ }
+ BnTo2B(bnT, &nonceK->b, 0);
+ T.t.size = CryptHashStart(&state, scheme->details.ecdaa.hashAlg);
+ if(T.t.size == 0)
+ {
+ retVal = TPM_RC_SCHEME;
+ }
+ else
+ {
+ CryptDigestUpdate2B(&state, &nonceK->b);
+ CryptDigestUpdate2B(&state, &digest->b);
+ CryptHashEnd2B(&state, &T.b);
+ BnFrom2B(bnT, &T.b);
+ // libtpms: Note: T is NOT a concern for constant-timeness
+ // Watch out for the name collisions in this call!!
+ retVal = BnSchnorrSign(bnS, bnR, bnT, bnD,
+ AccessCurveData(E)->order);
+ }
+ } while(retVal == TPM_RC_NO_RESULT);
+ // Because the rule is that internal state is not modified if the command
+ // fails, only end the commit if the command succeeds.
+ // NOTE that if the result of the Schnorr computation was zero
+ // it will probably not be worthwhile to run the same command again because
+ // the result will still be zero. This means that the Commit command will
+ // need to be run again to get a new commit value for the signature.
+ if(retVal == TPM_RC_SUCCESS)
+ CryptEndCommit(scheme->details.ecdaa.count);
+ }
+ return retVal;
+}
+#endif // ALG_ECDAA
+#if ALG_ECSCHNORR
+/* 10.2.12.3.3 SchnorrReduce() */
+/* Function to reduce a hash result if it's magnitude is to large. The size of number is set so that
+ it has no more bytes of significance than the reference value. If the resulting number can have
+ more bits of significance than the reference. */
+static void
+SchnorrReduce(
+ TPM2B *number, // IN/OUT: Value to reduce
+ bigConst reference // IN: the reference value
+ )
+{
+ UINT16 maxBytes = (UINT16)BITS_TO_BYTES(BnSizeInBits(reference));
+ if(number->size > maxBytes)
+ number->size = maxBytes;
+}
+/* 10.2.12.3.4 SchnorrEcc() */
+/* This function is used to perform a modified Schnorr signature. */
+/* This function will generate a random value k and compute */
+/* a) (xR, yR) = [k]G */
+/* b) r = hash(xR || P)(mod q) */
+/* c) rT = truncated r */
+/* d) s= k + rT * ds (mod q) */
+/* e) return the tuple rT, s */
+/* Error Returns Meaning */
+/* TPM_RC_NO_RESULT failure in the Schnorr sign process */
+/* TPM_RC_SCHEME hashAlg can't produce zero-length digest */
+static TPM_RC
+BnSignEcSchnorr(
+ bigNum bnR, // OUT: r component of the signature
+ bigNum bnS, // OUT: s component of the signature
+ bigCurve E, // IN: the curve used in signing
+ bigNum bnD, // IN: the signing key
+ const TPM2B_DIGEST *digest, // IN: the digest to sign
+ TPM_ALG_ID hashAlg, // IN: signing scheme (contains a hash)
+ RAND_STATE *rand // IN: non-NULL when testing
+ )
+{
+ HASH_STATE hashState;
+ UINT16 digestSize
+ = CryptHashGetDigestSize(hashAlg);
+ TPM2B_TYPE(T, MAX(MAX_DIGEST_SIZE, MAX_ECC_KEY_BYTES));
+ TPM2B_T T2b;
+ TPM2B *e = &T2b.b;
+ TPM_RC retVal = TPM_RC_NO_RESULT;
+ const ECC_CURVE_DATA *C;
+ bigConst order;
+ bigConst prime;
+ ECC_NUM(bnK);
+ POINT(ecR);
+ //
+ // Parameter checks
+ if(E == NULL)
+ ERROR_RETURN(TPM_RC_VALUE);
+ C = AccessCurveData(E);
+ order = CurveGetOrder(C);
+ prime = CurveGetOrder(C);
+ // If the digest does not produce a hash, then null the signature and return
+ // a failure.
+ if(digestSize == 0)
+ {
+ BnSetWord(bnR, 0);
+ BnSetWord(bnS, 0);
+ ERROR_RETURN(TPM_RC_SCHEME);
+ }
+ do
+ {
+ // Generate a random key pair
+ if(!BnEccGenerateKeyPair(bnK, ecR, E, rand))
+ break;
+ // Convert R.x to a string
+ BnTo2B(ecR->x, e, (NUMBYTES)BITS_TO_BYTES(BnSizeInBits(prime)));
+ // f) compute r = Hash(e || P) (mod n)
+ CryptHashStart(&hashState, hashAlg);
+ CryptDigestUpdate2B(&hashState, e);
+ CryptDigestUpdate2B(&hashState, &digest->b);
+ e->size = CryptHashEnd(&hashState, digestSize, e->buffer);
+ // Reduce the hash size if it is larger than the curve order
+ SchnorrReduce(e, order);
+ // Convert hash to number
+ BnFrom2B(bnR, e);
+ // libtpms: Note: e is NOT a concern for constant-timeness
+ // Do the Schnorr computation
+ retVal = BnSchnorrSign(bnS, bnK, bnR, bnD, CurveGetOrder(C));
+ } while(retVal == TPM_RC_NO_RESULT);
+ Exit:
+ return retVal;
+}
+#endif // ALG_ECSCHNORR
+#if ALG_SM2
+#ifdef _SM2_SIGN_DEBUG
+/* 10.2.12.3.5 BnHexEqual() */
+/* This function compares a bignum value to a hex string. */
+/* Return Value Meaning */
+/* TRUE(1) values equal */
+/* FALSE(0) values not equal */
+static BOOL
+BnHexEqual(
+ bigNum bn, //IN: big number value
+ const char *c //IN: character string number
+ )
+{
+ ECC_NUM(bnC);
+ BnFromHex(bnC, c);
+ return (BnUnsignedCmp(bn, bnC) == 0);
+}
+#endif // _SM2_SIGN_DEBUG
+/* 10.2.12.3.5 BnSignEcSm2() */
+/* This function signs a digest using the method defined in SM2 Part 2. The method in the standard
+ will add a header to the message to be signed that is a hash of the values that define the
+ key. This then hashed with the message to produce a digest (e) that is signed. This function
+ signs e. */
+/* Error Returns Meaning */
+/* TPM_RC_VALUE bad curve */
+static TPM_RC
+BnSignEcSm2(
+ bigNum bnR, // OUT: r component of the signature
+ bigNum bnS, // OUT: s component of the signature
+ bigCurve E, // IN: the curve used in signing
+ bigNum bnD, // IN: the private key
+ const TPM2B_DIGEST *digest, // IN: the digest to sign
+ RAND_STATE *rand // IN: random number generator (mostly for
+ // debug)
+ )
+{
+ BN_MAX_INITIALIZED(bnE, digest); // Don't know how big digest might be
+ ECC_NUM(bnN);
+ ECC_NUM(bnK);
+ ECC_NUM(bnT); // temp
+ POINT(Q1);
+ bigConst order = (E != NULL)
+ ? CurveGetOrder(AccessCurveData(E)) : NULL;
+// libtpms added begin
+ UINT32 orderBits = BnSizeInBits(order);
+ BOOL atByteBoundary = (orderBits & 7) == 0;
+ ECC_NUM(bnK1);
+// libtpms added end
+
+ //
+#ifdef _SM2_SIGN_DEBUG
+ BnFromHex(bnE, "B524F552CD82B8B028476E005C377FB1"
+ "9A87E6FC682D48BB5D42E3D9B9EFFE76");
+ BnFromHex(bnD, "128B2FA8BD433C6C068C8D803DFF7979"
+ "2A519A55171B1B650C23661D15897263");
+#endif
+ // A3: Use random number generator to generate random number 1 <= k <= n-1;
+ // NOTE: Ax: numbers are from the SM2 standard
+ loop:
+ {
+ // Get a random number 0 < k < n
+ // libtpms modified begin
+ //
+ // We take a dual approach here. One for curves whose order is not at
+ // the byte boundary, e.g. NIST P521, we get a random number bnK and add
+ // the order to that number to have bnK1. This will not spill over into
+ // a new byte and we can then use bnK1 to do the do the BnEccModMult
+ // with a constant number of bytes. For curves whose order is at the
+ // byte boundary we require that the random number bnK comes back with
+ // a requested number of bytes.
+ if (!atByteBoundary) {
+ BnGenerateRandomInRange(bnK, order, rand);
+ BnAdd(bnK1, bnK, order);
+#ifdef _SM2_SIGN_DEBUG
+ BnFromHex(bnK1, "6CB28D99385C175C94F94E934817663F"
+ "C176D925DD72B727260DBAAE1FB2F96F");
+#endif
+ // A4: Figure out the point of elliptic curve (x1, y1)=[k]G, and according
+ // to details specified in 4.2.7 in Part 1 of this document, transform the
+ // data type of x1 into an integer;
+ if(!BnEccModMult(Q1, NULL, bnK1, E))
+ goto loop;
+ } else {
+ BnGenerateRandomInRangeAllBytes(bnK, order, rand);
+#ifdef _SM2_SIGN_DEBUG
+ BnFromHex(bnK, "6CB28D99385C175C94F94E934817663F"
+ "C176D925DD72B727260DBAAE1FB2F96F");
+#endif
+ if(!BnEccModMult(Q1, NULL, bnK, E))
+ goto loop;
+ } // libtpms modified end
+ // A5: Figure out r = (e + x1) mod n,
+ BnAdd(bnR, bnE, Q1->x);
+ BnMod(bnR, order);
+#ifdef _SM2_SIGN_DEBUG
+ pAssert(BnHexEqual(bnR, "40F1EC59F793D9F49E09DCEF49130D41"
+ "94F79FB1EED2CAA55BACDB49C4E755D1"));
+#endif
+ // if r=0 or r+k=n, return to A3;
+ if(BnEqualZero(bnR))
+ goto loop;
+ BnAdd(bnT, bnK, bnR);
+ if(BnUnsignedCmp(bnT, bnN) == 0)
+ goto loop;
+ // A6: Figure out s = ((1 + dA)^-1 (k - r dA)) mod n,
+ // if s=0, return to A3;
+ // compute t = (1+dA)^-1
+ BnAddWord(bnT, bnD, 1);
+ BnModInverse(bnT, bnT, order);
+#ifdef _SM2_SIGN_DEBUG
+ pAssert(BnHexEqual(bnT, "79BFCF3052C80DA7B939E0C6914A18CB"
+ "B2D96D8555256E83122743A7D4F5F956"));
+#endif
+ // compute s = t * (k - r * dA) mod n
+ BnModMult(bnS, bnR, bnD, order);
+ // k - r * dA mod n = k + n - ((r * dA) mod n)
+ BnSub(bnS, order, bnS);
+ BnAdd(bnS, bnK, bnS);
+ BnModMult(bnS, bnS, bnT, order);
+#ifdef _SM2_SIGN_DEBUG
+ pAssert(BnHexEqual(bnS, "6FC6DAC32C5D5CF10C77DFB20F7C2EB6"
+ "67A457872FB09EC56327A67EC7DEEBE7"));
+#endif
+ if(BnEqualZero(bnS))
+ goto loop;
+ }
+ // A7: According to details specified in 4.2.1 in Part 1 of this document,
+ // transform the data type of r, s into bit strings, signature of message M
+ // is (r, s).
+ // This is handled by the common return code
+#ifdef _SM2_SIGN_DEBUG
+ pAssert(BnHexEqual(bnR, "40F1EC59F793D9F49E09DCEF49130D41"
+ "94F79FB1EED2CAA55BACDB49C4E755D1"));
+ pAssert(BnHexEqual(bnS, "6FC6DAC32C5D5CF10C77DFB20F7C2EB6"
+ "67A457872FB09EC56327A67EC7DEEBE7"));
+#endif
+ return TPM_RC_SUCCESS;
+}
+#endif // ALG_SM2
+/* 10.2.12.3.6 CryptEccSign() */
+/* This function is the dispatch function for the various ECC-based signing schemes. There is a bit
+ of ugliness to the parameter passing. In order to test this, we sometime would like to use a
+ deterministic RNG so that we can get the same signatures during testing. The easiest way to do
+ this for most schemes is to pass in a deterministic RNG and let it return canned values during
+ testing. There is a competing need for a canned parameter to use in ECDAA. To accommodate both
+ needs with minimal fuss, a special type of RAND_STATE is defined to carry the address of the
+ commit value. The setup and handling of this is not very different for the caller than what was
+ in previous versions of the code. */
+/* Error Returns Meaning */
+/* TPM_RC_SCHEME scheme is not supported */
+LIB_EXPORT TPM_RC
+CryptEccSign(
+ TPMT_SIGNATURE *signature, // OUT: signature
+ OBJECT *signKey, // IN: ECC key to sign the hash
+ const TPM2B_DIGEST *digest, // IN: digest to sign
+ TPMT_ECC_SCHEME *scheme, // IN: signing scheme
+ RAND_STATE *rand
+ )
+{
+ CURVE_INITIALIZED(E, signKey->publicArea.parameters.eccDetail.curveID);
+ ECC_INITIALIZED(bnD, &signKey->sensitive.sensitive.ecc.b);
+ ECC_NUM(bnR);
+ ECC_NUM(bnS);
+ const ECC_CURVE_DATA *C;
+ TPM_RC retVal = TPM_RC_SCHEME;
+ //
+ NOT_REFERENCED(scheme);
+ if(E == NULL)
+ ERROR_RETURN(TPM_RC_VALUE);
+ C = AccessCurveData(E);
+ signature->signature.ecdaa.signatureR.t.size
+ = sizeof(signature->signature.ecdaa.signatureR.t.buffer);
+ signature->signature.ecdaa.signatureS.t.size
+ = sizeof(signature->signature.ecdaa.signatureS.t.buffer);
+ TEST(signature->sigAlg);
+ switch(signature->sigAlg)
+ {
+ case TPM_ALG_ECDSA:
+ retVal = BnSignEcdsa(bnR, bnS, E, bnD, digest, rand);
+ break;
+#if ALG_ECDAA
+ case TPM_ALG_ECDAA:
+ retVal = BnSignEcdaa(&signature->signature.ecdaa.signatureR, bnS, E,
+ bnD, digest, scheme, signKey, rand);
+ bnR = NULL;
+ break;
+#endif
+#if ALG_ECSCHNORR
+ case TPM_ALG_ECSCHNORR:
+ retVal = BnSignEcSchnorr(bnR, bnS, E, bnD, digest,
+ signature->signature.ecschnorr.hash,
+ rand);
+ break;
+#endif
+#if ALG_SM2
+ case TPM_ALG_SM2:
+ retVal = BnSignEcSm2(bnR, bnS, E, bnD, digest, rand);
+ break;
+#endif
+ default:
+ break;
+ }
+ // If signature generation worked, convert the results.
+ if(retVal == TPM_RC_SUCCESS)
+ {
+ NUMBYTES orderBytes =
+ (NUMBYTES)BITS_TO_BYTES(BnSizeInBits(CurveGetOrder(C)));
+ if(bnR != NULL)
+ BnTo2B(bnR, &signature->signature.ecdaa.signatureR.b, orderBytes);
+ if(bnS != NULL)
+ BnTo2B(bnS, &signature->signature.ecdaa.signatureS.b, orderBytes);
+ }
+ Exit:
+ CURVE_FREE(E);
+ return retVal;
+}
+#if ALG_ECDSA
+/* 10.2.12.3.7 BnValidateSignatureEcdsa() */
+/* This function validates an ECDSA signature. rIn and sIn should have been checked to make sure
+ that they are in the range 0 < v < n */
+/* Error Returns Meaning */
+/* TPM_RC_SIGNATURE signature not valid */
+#if !USE_OPENSSL_FUNCTIONS_ECDSA // libtpms added
+TPM_RC
+BnValidateSignatureEcdsa(
+ bigNum bnR, // IN: r component of the signature
+ bigNum bnS, // IN: s component of the signature
+ bigCurve E, // IN: the curve used in the signature
+ // process
+ bn_point_t *ecQ, // IN: the public point of the key
+ const TPM2B_DIGEST *digest // IN: the digest that was signed
+ )
+{
+ // Make sure that the allocation for the digest is big enough for a maximum
+ // digest
+ BN_VAR(bnE, MAX(MAX_ECC_KEY_BYTES, MAX_DIGEST_SIZE) * 8);
+ POINT(ecR);
+ ECC_NUM(bnU1);
+ ECC_NUM(bnU2);
+ ECC_NUM(bnW);
+ bigConst order = CurveGetOrder(AccessCurveData(E));
+ TPM_RC retVal = TPM_RC_SIGNATURE;
+ // Get adjusted digest
+ EcdsaDigest(bnE, digest, order);
+ // 1. If r and s are not both integers in the interval [1, n - 1], output
+ // INVALID.
+ // bnR and bnS were validated by the caller
+ // 2. Use the selected hash function to compute H0 = Hash(M0).
+ // This is an input parameter
+ // 3. Convert the bit string H0 to an integer e as described in Appendix B.2.
+ // Done at entry
+ // 4. Compute w = (s')^-1 mod n, using the routine in Appendix B.1.
+ if(!BnModInverse(bnW, bnS, order))
+ goto Exit;
+ // 5. Compute u1 = (e' * w) mod n, and compute u2 = (r' * w) mod n.
+ BnModMult(bnU1, bnE, bnW, order);
+ BnModMult(bnU2, bnR, bnW, order);
+ // 6. Compute the elliptic curve point R = (xR, yR) = u1G+u2Q, using EC
+ // scalar multiplication and EC addition (see [Routines]). If R is equal to
+ // the point at infinity O, output INVALID.
+ if(BnPointMult(ecR, CurveGetG(AccessCurveData(E)), bnU1, ecQ, bnU2, E)
+ != TPM_RC_SUCCESS)
+ goto Exit;
+ // 7. Compute v = Rx mod n.
+ BnMod(ecR->x, order);
+ // 8. Compare v and r0. If v = r0, output VALID; otherwise, output INVALID
+ if(BnUnsignedCmp(ecR->x, bnR) != 0)
+ goto Exit;
+ retVal = TPM_RC_SUCCESS;
+ Exit:
+ return retVal;
+}
+#else // USE_OPENSSL_FUNCTIONS_ECDSA libtpms added begin
+TPM_RC
+BnValidateSignatureEcdsa(
+ bigNum bnR, // IN: r component of the signature
+ bigNum bnS, // IN: s component of the signature
+ bigCurve E, // IN: the curve used in the signature
+ // process
+ bn_point_t *ecQ, // IN: the public point of the key
+ const TPM2B_DIGEST *digest // IN: the digest that was signed
+ )
+{
+ int retVal;
+ int rc;
+ ECDSA_SIG *sig = NULL;
+ EC_KEY *eckey = NULL;
+ BIGNUM *r = BN_new();
+ BIGNUM *s = BN_new();
+ EC_POINT *q = EcPointInitialized(ecQ, E);
+
+ r = BigInitialized(r, bnR);
+ s = BigInitialized(s, bnS);
+
+ sig = ECDSA_SIG_new();
+ eckey = EC_KEY_new();
+
+ if (r == NULL || s == NULL || q == NULL || sig == NULL || eckey == NULL)
+ ERROR_RETURN(TPM_RC_FAILURE);
+
+ if (EC_KEY_set_group(eckey, E->G) != 1)
+ ERROR_RETURN(TPM_RC_FAILURE);
+
+ if (EC_KEY_set_public_key(eckey, q) != 1)
+ ERROR_RETURN(TPM_RC_FAILURE);
+
+ if (ECDSA_SIG_set0(sig, r, s) != 1)
+ ERROR_RETURN(TPM_RC_FAILURE);
+
+ /* sig now owns r and s */
+ r = NULL;
+ s = NULL;
+
+ rc = ECDSA_do_verify(digest->b.buffer, digest->b.size, sig, eckey);
+ switch (rc) {
+ case 1:
+ retVal = TPM_RC_SUCCESS;
+ break;
+ case 0:
+ retVal = TPM_RC_SIGNATURE;
+ break;
+ default:
+ retVal = TPM_RC_FAILURE;
+ break;
+ }
+
+ Exit:
+ EC_KEY_free(eckey);
+ ECDSA_SIG_free(sig);
+ EC_POINT_clear_free(q);
+ BN_clear_free(r);
+ BN_clear_free(s);
+
+ return retVal;
+}
+#endif // USE_OPENSSL_FUNCTIONS_ECDSA libtpms added end
+#endif // ALG_ECDSA
+#if ALG_SM2
+/* 10.2.12.3.8 BnValidateSignatureEcSm2() */
+/* This function is used to validate an SM2 signature. */
+/* Error Returns Meaning */
+/* TPM_RC_SIGNATURE signature not valid */
+static TPM_RC
+BnValidateSignatureEcSm2(
+ bigNum bnR, // IN: r component of the signature
+ bigNum bnS, // IN: s component of the signature
+ bigCurve E, // IN: the curve used in the signature
+ // process
+ bigPoint ecQ, // IN: the public point of the key
+ const TPM2B_DIGEST *digest // IN: the digest that was signed
+ )
+{
+ POINT(P);
+ ECC_NUM(bnRp);
+ ECC_NUM(bnT);
+ BN_MAX_INITIALIZED(bnE, digest);
+ BOOL OK;
+ bigConst order = CurveGetOrder(AccessCurveData(E));
+#ifdef _SM2_SIGN_DEBUG
+ // Make sure that the input signature is the test signature
+ pAssert(BnHexEqual(bnR,
+ "40F1EC59F793D9F49E09DCEF49130D41"
+ "94F79FB1EED2CAA55BACDB49C4E755D1"));
+ pAssert(BnHexEqual(bnS,
+ "6FC6DAC32C5D5CF10C77DFB20F7C2EB6"
+ "67A457872FB09EC56327A67EC7DEEBE7"));
+#endif
+ // b) compute t := (r + s) mod n
+ BnAdd(bnT, bnR, bnS);
+ BnMod(bnT, order);
+#ifdef _SM2_SIGN_DEBUG
+ pAssert(BnHexEqual(bnT,
+ "2B75F07ED7ECE7CCC1C8986B991F441A"
+ "D324D6D619FE06DD63ED32E0C997C801"));
+#endif
+ // c) verify that t > 0
+ OK = !BnEqualZero(bnT);
+ if(!OK)
+ // set T to a value that should allow rest of the computations to run
+ // without trouble
+ BnCopy(bnT, bnS);
+ // d) compute (x, y) := [s]G + [t]Q
+ OK = BnEccModMult2(P, NULL, bnS, ecQ, bnT, E);
+#ifdef _SM2_SIGN_DEBUG
+ pAssert(OK && BnHexEqual(P->x,
+ "110FCDA57615705D5E7B9324AC4B856D"
+ "23E6D9188B2AE47759514657CE25D112"));
+#endif
+ // e) compute r' := (e + x) mod n (the x coordinate is in bnT)
+ OK = OK && BnAdd(bnRp, bnE, P->x);
+ OK = OK && BnMod(bnRp, order);
+ // f) verify that r' = r
+ OK = OK && (BnUnsignedCmp(bnR, bnRp) == 0);
+
+ if(!OK)
+ return TPM_RC_SIGNATURE;
+ else
+ return TPM_RC_SUCCESS;
+}
+#endif // ALG_SM2
+#if ALG_ECSCHNORR
+/* 10.2.12.3.9 BnValidateSignatureEcSchnorr() */
+/* This function is used to validate an EC Schnorr signature. */
+/* Error Returns Meaning */
+/* TPM_RC_SIGNATURE signature not valid */
+static TPM_RC
+BnValidateSignatureEcSchnorr(
+ bigNum bnR, // IN: r component of the signature
+ bigNum bnS, // IN: s component of the signature
+ TPM_ALG_ID hashAlg, // IN: hash algorithm of the signature
+ bigCurve E, // IN: the curve used in the signature
+ // process
+ bigPoint ecQ, // IN: the public point of the key
+ const TPM2B_DIGEST *digest // IN: the digest that was signed
+ )
+{
+ BN_MAX(bnRn);
+ POINT(ecE);
+ BN_MAX(bnEx);
+ const ECC_CURVE_DATA *C = AccessCurveData(E);
+ bigConst order = CurveGetOrder(C);
+ UINT16 digestSize = CryptHashGetDigestSize(hashAlg);
+ HASH_STATE hashState;
+ TPM2B_TYPE(BUFFER, MAX(MAX_ECC_PARAMETER_BYTES, MAX_DIGEST_SIZE));
+ TPM2B_BUFFER Ex2 = {{sizeof(Ex2.t.buffer),{ 0 }}};
+ BOOL OK;
+ //
+ // E = [s]G - [r]Q
+ BnMod(bnR, order);
+ // Make -r = n - r
+ BnSub(bnRn, order, bnR);
+ // E = [s]G + [-r]Q
+ OK = BnPointMult(ecE, CurveGetG(C), bnS, ecQ, bnRn, E) == TPM_RC_SUCCESS;
+ // // reduce the x portion of E mod q
+ // OK = OK && BnMod(ecE->x, order);
+ // Convert to byte string
+ OK = OK && BnTo2B(ecE->x, &Ex2.b,
+ (NUMBYTES)(BITS_TO_BYTES(BnSizeInBits(order))));
+ if(OK)
+ {
+ // Ex = h(pE.x || digest)
+ CryptHashStart(&hashState, hashAlg);
+ CryptDigestUpdate(&hashState, Ex2.t.size, Ex2.t.buffer);
+ CryptDigestUpdate(&hashState, digest->t.size, digest->t.buffer);
+ Ex2.t.size = CryptHashEnd(&hashState, digestSize, Ex2.t.buffer);
+ SchnorrReduce(&Ex2.b, order);
+ BnFrom2B(bnEx, &Ex2.b);
+ // see if Ex matches R
+ OK = BnUnsignedCmp(bnEx, bnR) == 0;
+ }
+ return (OK) ? TPM_RC_SUCCESS : TPM_RC_SIGNATURE;
+}
+#endif // ALG_ECSCHNORR
+/* 10.2.12.3.10 CryptEccValidateSignature() */
+/* This function validates an EcDsa() or EcSchnorr() signature. The point Qin needs to have been
+ validated to be on the curve of curveId. */
+/* Error Returns Meaning */
+/* TPM_RC_SIGNATURE not a valid signature */
+LIB_EXPORT TPM_RC
+CryptEccValidateSignature(
+ TPMT_SIGNATURE *signature, // IN: signature to be verified
+ OBJECT *signKey, // IN: ECC key signed the hash
+ const TPM2B_DIGEST *digest // IN: digest that was signed
+ )
+{
+ CURVE_INITIALIZED(E, signKey->publicArea.parameters.eccDetail.curveID);
+ ECC_NUM(bnR);
+ ECC_NUM(bnS);
+ POINT_INITIALIZED(ecQ, &signKey->publicArea.unique.ecc);
+ bigConst order;
+ TPM_RC retVal;
+ if(E == NULL)
+ ERROR_RETURN(TPM_RC_VALUE);
+ order = CurveGetOrder(AccessCurveData(E));
+ // // Make sure that the scheme is valid
+ switch(signature->sigAlg)
+ {
+ case TPM_ALG_ECDSA:
+#if ALG_ECSCHNORR
+ case TPM_ALG_ECSCHNORR:
+#endif
+#if ALG_SM2
+ case TPM_ALG_SM2:
+#endif
+ break;
+ default:
+ ERROR_RETURN(TPM_RC_SCHEME);
+ break;
+ }
+ // Can convert r and s after determining that the scheme is an ECC scheme. If
+ // this conversion doesn't work, it means that the unmarshaling code for
+ // an ECC signature is broken.
+ BnFrom2B(bnR, &signature->signature.ecdsa.signatureR.b);
+ BnFrom2B(bnS, &signature->signature.ecdsa.signatureS.b);
+ // r and s have to be greater than 0 but less than the curve order
+ if(BnEqualZero(bnR) || BnEqualZero(bnS))
+ ERROR_RETURN(TPM_RC_SIGNATURE);
+ if((BnUnsignedCmp(bnS, order) >= 0)
+ || (BnUnsignedCmp(bnR, order) >= 0))
+ ERROR_RETURN(TPM_RC_SIGNATURE);
+ switch(signature->sigAlg)
+ {
+ case TPM_ALG_ECDSA:
+ retVal = BnValidateSignatureEcdsa(bnR, bnS, E, ecQ, digest);
+ break;
+#if ALG_ECSCHNORR
+ case TPM_ALG_ECSCHNORR:
+ retVal = BnValidateSignatureEcSchnorr(bnR, bnS,
+ signature->signature.any.hashAlg,
+ E, ecQ, digest);
+ break;
+#endif
+#if ALG_SM2
+ case TPM_ALG_SM2:
+ retVal = BnValidateSignatureEcSm2(bnR, bnS, E, ecQ, digest);
+ break;
+#endif
+ default:
+ FAIL(FATAL_ERROR_INTERNAL);
+ }
+ Exit:
+ CURVE_FREE(E);
+ return retVal;
+}
+/* 10.2.12.3.11 CryptEccCommitCompute() */
+/* This function performs the point multiply operations required by TPM2_Commit(). */
+/* If B or M is provided, they must be on the curve defined by curveId. This routine does not check
+ that they are on the curve and results are unpredictable if they are not. */
+/* It is a fatal error if r is NULL. If B is not NULL, then it is a fatal error if d is NULL or if K
+ and L are both NULL. If M is not NULL, then it is a fatal error if E is NULL. */
+/* Error Returns Meaning */
+/* TPM_RC_NO_RESULT if K, L or E was computed to be the point at infinity */
+/* TPM_RC_CANCELED a cancel indication was asserted during this function */
+LIB_EXPORT TPM_RC
+CryptEccCommitCompute(
+ TPMS_ECC_POINT *K, // OUT: [d]B or [r]Q
+ TPMS_ECC_POINT *L, // OUT: [r]B
+ TPMS_ECC_POINT *E, // OUT: [r]M
+ TPM_ECC_CURVE curveId, // IN: the curve for the computations
+ TPMS_ECC_POINT *M, // IN: M (optional)
+ TPMS_ECC_POINT *B, // IN: B (optional)
+ TPM2B_ECC_PARAMETER *d, // IN: d (optional)
+ TPM2B_ECC_PARAMETER *r // IN: the computed r value (required)
+ )
+{
+ CURVE_INITIALIZED(curve, curveId); // Normally initialize E as the curve, but E means
+ // something else in this function
+ ECC_INITIALIZED(bnR, r);
+ TPM_RC retVal = TPM_RC_SUCCESS;
+ //
+ // Validate that the required parameters are provided.
+ // Note: E has to be provided if computing E := [r]Q or E := [r]M. Will do
+ // E := [r]Q if both M and B are NULL.
+ pAssert(r != NULL && E != NULL);
+ // Initialize the output points in case they are not computed
+ ClearPoint2B(K);
+ ClearPoint2B(L);
+ ClearPoint2B(E);
+ // Sizes of the r parameter may not be zero
+ pAssert(r->t.size > 0);
+ // If B is provided, compute K=[d]B and L=[r]B
+ if(B != NULL)
+ {
+ ECC_INITIALIZED(bnD, d);
+ POINT_INITIALIZED(pB, B);
+ POINT(pK);
+ POINT(pL);
+ //
+ pAssert(d != NULL && K != NULL && L != NULL);
+ if(!BnIsOnCurve(pB, AccessCurveData(curve)))
+ ERROR_RETURN(TPM_RC_VALUE);
+ // do the math for K = [d]B
+ if((retVal = BnPointMult(pK, pB, bnD, NULL, NULL, curve)) != TPM_RC_SUCCESS)
+ goto Exit;
+ // Convert BN K to TPM2B K
+ BnPointTo2B(K, pK, curve);
+ // compute L= [r]B after checking for cancel
+ if(_plat__IsCanceled())
+ ERROR_RETURN(TPM_RC_CANCELED);
+ // compute L = [r]B
+ if(!BnIsValidPrivateEcc(bnR, curve))
+ ERROR_RETURN(TPM_RC_VALUE);
+ if((retVal = BnPointMult(pL, pB, bnR, NULL, NULL, curve)) != TPM_RC_SUCCESS)
+ goto Exit;
+ // Convert BN L to TPM2B L
+ BnPointTo2B(L, pL, curve);
+ }
+ if((M != NULL) || (B == NULL))
+ {
+ POINT_INITIALIZED(pM, M);
+ POINT(pE);
+ //
+ // Make sure that a place was provided for the result
+ pAssert(E != NULL);
+ // if this is the third point multiply, check for cancel first
+ if((B != NULL) && _plat__IsCanceled())
+ ERROR_RETURN(TPM_RC_CANCELED);
+ // If M provided, then pM will not be NULL and will compute E = [r]M.
+ // However, if M was not provided, then pM will be NULL and E = [r]G
+ // will be computed
+ if((retVal = BnPointMult(pE, pM, bnR, NULL, NULL, curve)) != TPM_RC_SUCCESS)
+ goto Exit;
+ // Convert E to 2B format
+ BnPointTo2B(E, pE, curve);
+ }
+ Exit:
+ CURVE_FREE(curve);
+ return retVal;
+}
+#endif // TPM_ALG_ECC