/* 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. */ //** Includes and Defines #include "Tpm.h" #include "CryptEccSignature_fp.h" #if ALG_ECC //** Utility Functions //*** 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. 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; } //*** 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 // 1) 's' is the signature // 2) 'k' is a random value // 3) 'r' is the value to sign // 4) 'd' is the private EC key // 5) 'n' is the order of the curve // Return Type: TPM_RC // 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; } //** Signing Functions //*** BnSignEcdsa() // This function implements the ECDSA signing algorithm. The method is described // in the comments below. 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; } #if ALG_ECDAA //*** BnSignEcdaa() // // This function performs 's' = 'r' + 'T' * 'd' mod 'q' where // 1) 'r' is a random, or pseudo-random value created in the commit phase // 2) 'nonceK' is a TPM-generated, random value 0 < 'nonceK' < 'n' // 3) 'T' is mod 'q' of "Hash"('nonceK' || 'digest'), and // 4) '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. // Return Type: TPM_RC // 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(!BnEccGetPrivate(bnT, AccessCurveData(E), rand)) { 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); // 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 //*** SchnorrReduce() // Function to reduce a hash result if it's magnitude is too large. The size of // 'number' is set so that it has no more bytes of significance than 'reference' // value. If the resulting number can have more bits of significance than // '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; } //*** 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' // // Return Type: TPM_RC // 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); // 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 //*** BnHexEqual() // This function compares a bignum value to a hex string. // Return Type: BOOL // 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 //*** 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'). This function signs 'e'. // Return Type: TPM_RC // 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; // #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 BnGenerateRandomInRange(bnK, order, rand); #ifdef _SM2_SIGN_DEBUG BnFromHex(bnK, "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, bnK, E)) goto loop; // 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 //*** 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. // Return Type: TPM_RC // 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; } //********************* Signature Validation ******************** #if ALG_ECDSA //*** 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' // Return Type: TPM_RC // TPM_RC_SIGNATURE signature not valid 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; } #endif // ALG_ECDSA #if ALG_SM2 //*** BnValidateSignatureEcSm2() // This function is used to validate an SM2 signature. // Return Type: TPM_RC // 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 //*** BnValidateSignatureEcSchnorr() // This function is used to validate an EC Schnorr signature. // Return Type: TPM_RC // 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 //*** CryptEccValidateSignature() // This function validates an EcDsa or EcSchnorr signature. // The point 'Qin' needs to have been validated to be on the curve of 'curveId'. // Return Type: TPM_RC // 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; } //***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. // // Return Type: TPM_RC // 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 // ALG_ECC