/********************************************************************************/ /* */ /* 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" #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. */ 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; } /* 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. */ 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 /* 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(!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 /* 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); // 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; // #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 /* 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 */ 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 /* 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