1 | /********************************************************************************/
|
---|
2 | /* */
|
---|
3 | /* ECC Main */
|
---|
4 | /* Written by Ken Goldman */
|
---|
5 | /* IBM Thomas J. Watson Research Center */
|
---|
6 | /* */
|
---|
7 | /* Licenses and Notices */
|
---|
8 | /* */
|
---|
9 | /* 1. Copyright Licenses: */
|
---|
10 | /* */
|
---|
11 | /* - Trusted Computing Group (TCG) grants to the user of the source code in */
|
---|
12 | /* this specification (the "Source Code") a worldwide, irrevocable, */
|
---|
13 | /* nonexclusive, royalty free, copyright license to reproduce, create */
|
---|
14 | /* derivative works, distribute, display and perform the Source Code and */
|
---|
15 | /* derivative works thereof, and to grant others the rights granted herein. */
|
---|
16 | /* */
|
---|
17 | /* - The TCG grants to the user of the other parts of the specification */
|
---|
18 | /* (other than the Source Code) the rights to reproduce, distribute, */
|
---|
19 | /* display, and perform the specification solely for the purpose of */
|
---|
20 | /* developing products based on such documents. */
|
---|
21 | /* */
|
---|
22 | /* 2. Source Code Distribution Conditions: */
|
---|
23 | /* */
|
---|
24 | /* - Redistributions of Source Code must retain the above copyright licenses, */
|
---|
25 | /* this list of conditions and the following disclaimers. */
|
---|
26 | /* */
|
---|
27 | /* - Redistributions in binary form must reproduce the above copyright */
|
---|
28 | /* licenses, this list of conditions and the following disclaimers in the */
|
---|
29 | /* documentation and/or other materials provided with the distribution. */
|
---|
30 | /* */
|
---|
31 | /* 3. Disclaimers: */
|
---|
32 | /* */
|
---|
33 | /* - THE COPYRIGHT LICENSES SET FORTH ABOVE DO NOT REPRESENT ANY FORM OF */
|
---|
34 | /* LICENSE OR WAIVER, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, WITH */
|
---|
35 | /* RESPECT TO PATENT RIGHTS HELD BY TCG MEMBERS (OR OTHER THIRD PARTIES) */
|
---|
36 | /* THAT MAY BE NECESSARY TO IMPLEMENT THIS SPECIFICATION OR OTHERWISE. */
|
---|
37 | /* Contact TCG Administration ([email protected]) for */
|
---|
38 | /* information on specification licensing rights available through TCG */
|
---|
39 | /* membership agreements. */
|
---|
40 | /* */
|
---|
41 | /* - THIS SPECIFICATION IS PROVIDED "AS IS" WITH NO EXPRESS OR IMPLIED */
|
---|
42 | /* WARRANTIES WHATSOEVER, INCLUDING ANY WARRANTY OF MERCHANTABILITY OR */
|
---|
43 | /* FITNESS FOR A PARTICULAR PURPOSE, ACCURACY, COMPLETENESS, OR */
|
---|
44 | /* NONINFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS, OR ANY WARRANTY */
|
---|
45 | /* OTHERWISE ARISING OUT OF ANY PROPOSAL, SPECIFICATION OR SAMPLE. */
|
---|
46 | /* */
|
---|
47 | /* - Without limitation, TCG and its members and licensors disclaim all */
|
---|
48 | /* liability, including liability for infringement of any proprietary */
|
---|
49 | /* rights, relating to use of information in this specification and to the */
|
---|
50 | /* implementation of this specification, and TCG disclaims all liability for */
|
---|
51 | /* cost of procurement of substitute goods or services, lost profits, loss */
|
---|
52 | /* of use, loss of data or any incidental, consequential, direct, indirect, */
|
---|
53 | /* or special damages, whether under contract, tort, warranty or otherwise, */
|
---|
54 | /* arising in any way out of use or reliance upon this specification or any */
|
---|
55 | /* information herein. */
|
---|
56 | /* */
|
---|
57 | /* (c) Copyright IBM Corp. and others, 2016 - 2024 */
|
---|
58 | /* */
|
---|
59 | /********************************************************************************/
|
---|
60 |
|
---|
61 | //** Includes and Defines
|
---|
62 | #include "Tpm.h"
|
---|
63 | #include "TpmMath_Util_fp.h"
|
---|
64 | #include "TpmEcc_Util_fp.h"
|
---|
65 | #include "TpmEcc_Signature_ECDSA_fp.h" // required for pairwise test in key generation
|
---|
66 | #include "Helpers_fp.h" // libtpms added
|
---|
67 | #if ALG_ECC
|
---|
68 | //** Functions
|
---|
69 |
|
---|
70 | # if SIMULATION
|
---|
71 | void EccSimulationEnd(void)
|
---|
72 | {
|
---|
73 | # if SIMULATION
|
---|
74 | // put things to be printed at the end of the simulation here
|
---|
75 | # endif
|
---|
76 | }
|
---|
77 | # endif // SIMULATION
|
---|
78 |
|
---|
79 | //*** CryptEccInit()
|
---|
80 | // This function is called at _TPM_Init
|
---|
81 | BOOL CryptEccInit(void)
|
---|
82 | {
|
---|
83 | return TRUE;
|
---|
84 | }
|
---|
85 |
|
---|
86 | //*** CryptEccStartup()
|
---|
87 | // This function is called at TPM2_Startup().
|
---|
88 | BOOL CryptEccStartup(void)
|
---|
89 | {
|
---|
90 | return TRUE;
|
---|
91 | }
|
---|
92 |
|
---|
93 | //*** ClearPoint2B(generic)
|
---|
94 | // Initialize the size values of a TPMS_ECC_POINT structure.
|
---|
95 | void ClearPoint2B(TPMS_ECC_POINT* p // IN: the point
|
---|
96 | )
|
---|
97 | {
|
---|
98 | if(p != NULL)
|
---|
99 | {
|
---|
100 | p->x.t.size = 0;
|
---|
101 | p->y.t.size = 0;
|
---|
102 | }
|
---|
103 | }
|
---|
104 |
|
---|
105 | //*** CryptEccGetParametersByCurveId()
|
---|
106 | // This function returns a pointer to the curve data that is associated with
|
---|
107 | // the indicated curveId.
|
---|
108 | // If there is no curve with the indicated ID, the function returns NULL. This
|
---|
109 | // function is in this module so that it can be called by GetCurve data.
|
---|
110 | // Return Type: const TPM_ECC_CURVE_METADATA
|
---|
111 | // NULL curve with the indicated TPM_ECC_CURVE is not implemented
|
---|
112 | // != NULL pointer to the curve data
|
---|
113 | LIB_EXPORT const TPM_ECC_CURVE_METADATA* CryptEccGetParametersByCurveId(
|
---|
114 | TPM_ECC_CURVE curveId // IN: the curveID
|
---|
115 | )
|
---|
116 | {
|
---|
117 | int i;
|
---|
118 | for(i = 0; i < ECC_CURVE_COUNT; i++)
|
---|
119 | {
|
---|
120 | if(eccCurves[i].curveId == curveId)
|
---|
121 | return &eccCurves[i];
|
---|
122 | }
|
---|
123 | return NULL;
|
---|
124 | }
|
---|
125 |
|
---|
126 | //*** CryptEccGetKeySizeForCurve()
|
---|
127 | // This function returns the key size in bits of the indicated curve.
|
---|
128 | LIB_EXPORT UINT16 CryptEccGetKeySizeForCurve(TPM_ECC_CURVE curveId // IN: the curve
|
---|
129 | )
|
---|
130 | {
|
---|
131 | const TPM_ECC_CURVE_METADATA* curve = CryptEccGetParametersByCurveId(curveId);
|
---|
132 | UINT16 keySizeInBits;
|
---|
133 | //
|
---|
134 | keySizeInBits = (curve != NULL) ? curve->keySizeBits : 0;
|
---|
135 | return keySizeInBits;
|
---|
136 | }
|
---|
137 |
|
---|
138 | //***CryptEccGetOID()
|
---|
139 | const BYTE* CryptEccGetOID(TPM_ECC_CURVE curveId)
|
---|
140 | {
|
---|
141 | const TPM_ECC_CURVE_METADATA* curve = CryptEccGetParametersByCurveId(curveId);
|
---|
142 | return (curve != NULL) ? curve->OID : NULL;
|
---|
143 | }
|
---|
144 |
|
---|
145 | //*** CryptEccGetCurveByIndex()
|
---|
146 | // This function returns the number of the 'i'-th implemented curve. The normal
|
---|
147 | // use would be to call this function with 'i' starting at 0. When the 'i' is greater
|
---|
148 | // than or equal to the number of implemented curves, TPM_ECC_NONE is returned.
|
---|
149 | LIB_EXPORT TPM_ECC_CURVE CryptEccGetCurveByIndex(UINT16 i)
|
---|
150 | {
|
---|
151 | if(i >= ECC_CURVE_COUNT)
|
---|
152 | return TPM_ECC_NONE;
|
---|
153 | return eccCurves[i].curveId;
|
---|
154 | }
|
---|
155 |
|
---|
156 | //*** CryptCapGetECCCurve()
|
---|
157 | // This function returns the list of implemented ECC curves.
|
---|
158 | // Return Type: TPMI_YES_NO
|
---|
159 | // YES if no more ECC curve is available
|
---|
160 | // NO if there are more ECC curves not reported
|
---|
161 | TPMI_YES_NO
|
---|
162 | CryptCapGetECCCurve(TPM_ECC_CURVE curveID, // IN: the starting ECC curve
|
---|
163 | UINT32 maxCount, // IN: count of returned curves
|
---|
164 | TPML_ECC_CURVE* curveList // OUT: ECC curve list
|
---|
165 | )
|
---|
166 | {
|
---|
167 | TPMI_YES_NO more = NO;
|
---|
168 | UINT16 i;
|
---|
169 | UINT32 count = ECC_CURVE_COUNT;
|
---|
170 | TPM_ECC_CURVE curve;
|
---|
171 |
|
---|
172 | // Initialize output property list
|
---|
173 | curveList->count = 0;
|
---|
174 |
|
---|
175 | // The maximum count of curves we may return is MAX_ECC_CURVES
|
---|
176 | if(maxCount > MAX_ECC_CURVES)
|
---|
177 | maxCount = MAX_ECC_CURVES;
|
---|
178 |
|
---|
179 | // Scan the eccCurveValues array
|
---|
180 | for(i = 0; i < count; i++)
|
---|
181 | {
|
---|
182 | curve = CryptEccGetCurveByIndex(i);
|
---|
183 | // If curveID is less than the starting curveID, skip it
|
---|
184 | if(curve < curveID)
|
---|
185 | continue;
|
---|
186 | if (!CryptEccIsCurveRuntimeUsable(curve)) // libtpms added begin
|
---|
187 | continue;
|
---|
188 | if (!RuntimeAlgorithmKeySizeCheckEnabled(&g_RuntimeProfile.RuntimeAlgorithm,
|
---|
189 | TPM_ALG_ECC,
|
---|
190 | CryptEccGetKeySizeForCurve(curve),
|
---|
191 | curve,
|
---|
192 | g_RuntimeProfile.stateFormatLevel))
|
---|
193 | continue; // libtpms added end
|
---|
194 | if(curveList->count < maxCount)
|
---|
195 | {
|
---|
196 | // If we have not filled up the return list, add more curves to
|
---|
197 | // it
|
---|
198 | curveList->eccCurves[curveList->count] = curve;
|
---|
199 | curveList->count++;
|
---|
200 | }
|
---|
201 | else
|
---|
202 | {
|
---|
203 | // If the return list is full but we still have curves
|
---|
204 | // available, report this and stop iterating
|
---|
205 | more = YES;
|
---|
206 | break;
|
---|
207 | }
|
---|
208 | }
|
---|
209 | return more;
|
---|
210 | }
|
---|
211 |
|
---|
212 | //*** CryptCapGetOneECCCurve()
|
---|
213 | // This function returns whether the ECC curve is implemented.
|
---|
214 | BOOL CryptCapGetOneECCCurve(TPM_ECC_CURVE curveID // IN: the ECC curve
|
---|
215 | )
|
---|
216 | {
|
---|
217 | UINT16 i;
|
---|
218 |
|
---|
219 | if (!CryptEccIsCurveRuntimeUsable(curveID) || // libtpms added begin
|
---|
220 | !RuntimeAlgorithmKeySizeCheckEnabled(&g_RuntimeProfile.RuntimeAlgorithm,
|
---|
221 | TPM_ALG_ECC,
|
---|
222 | CryptEccGetKeySizeForCurve(curveID),
|
---|
223 | curveID,
|
---|
224 | g_RuntimeProfile.stateFormatLevel))
|
---|
225 | return FALSE; // libtpms added end
|
---|
226 |
|
---|
227 | // Scan the eccCurveValues array
|
---|
228 | for(i = 0; i < ECC_CURVE_COUNT; i++)
|
---|
229 | {
|
---|
230 | if(CryptEccGetCurveByIndex(i) == curveID)
|
---|
231 | {
|
---|
232 | return TRUE;
|
---|
233 | }
|
---|
234 | }
|
---|
235 | return FALSE;
|
---|
236 | }
|
---|
237 |
|
---|
238 | //*** CryptGetCurveSignScheme()
|
---|
239 | // This function will return a pointer to the scheme of the curve.
|
---|
240 | const TPMT_ECC_SCHEME* CryptGetCurveSignScheme(
|
---|
241 | TPM_ECC_CURVE curveId // IN: The curve selector
|
---|
242 | )
|
---|
243 | {
|
---|
244 | const TPM_ECC_CURVE_METADATA* curve = CryptEccGetParametersByCurveId(curveId);
|
---|
245 |
|
---|
246 | if(curve != NULL)
|
---|
247 | return &(curve->sign);
|
---|
248 | else
|
---|
249 | return NULL;
|
---|
250 | }
|
---|
251 |
|
---|
252 | //*** CryptGenerateR()
|
---|
253 | // This function computes the commit random value for a split signing scheme.
|
---|
254 | //
|
---|
255 | // If 'c' is NULL, it indicates that 'r' is being generated
|
---|
256 | // for TPM2_Commit.
|
---|
257 | // If 'c' is not NULL, the TPM will validate that the 'gr.commitArray'
|
---|
258 | // bit associated with the input value of 'c' is SET. If not, the TPM
|
---|
259 | // returns FALSE and no 'r' value is generated.
|
---|
260 | // Return Type: BOOL
|
---|
261 | // TRUE(1) r value computed
|
---|
262 | // FALSE(0) no r value computed
|
---|
263 | BOOL CryptGenerateR(TPM2B_ECC_PARAMETER* r, // OUT: the generated random value
|
---|
264 | UINT16* c, // IN/OUT: count value.
|
---|
265 | TPMI_ECC_CURVE curveID, // IN: the curve for the value
|
---|
266 | TPM2B_NAME* name // IN: optional name of a key to
|
---|
267 | // associate with 'r'
|
---|
268 | )
|
---|
269 | {
|
---|
270 | // This holds the marshaled g_commitCounter.
|
---|
271 | TPM2B_TYPE(8B, 8);
|
---|
272 | TPM2B_8B cntr = {{8, {0}}};
|
---|
273 | UINT32 iterations;
|
---|
274 | TPM2B_ECC_PARAMETER n;
|
---|
275 | UINT64 currentCount = gr.commitCounter;
|
---|
276 | UINT16 t1;
|
---|
277 | //
|
---|
278 | if(!TpmMath_IntTo2B(ExtEcc_CurveGetOrder(curveID), (TPM2B*)&n, 0))
|
---|
279 | return FALSE;
|
---|
280 |
|
---|
281 | // If this is the commit phase, use the current value of the commit counter
|
---|
282 | if(c != NULL)
|
---|
283 | {
|
---|
284 | // if the array bit is not set, can't use the value.
|
---|
285 | if(!TEST_BIT((*c & COMMIT_INDEX_MASK), gr.commitArray))
|
---|
286 | return FALSE;
|
---|
287 |
|
---|
288 | // If it is the sign phase, figure out what the counter value was
|
---|
289 | // when the commitment was made.
|
---|
290 | //
|
---|
291 | // When gr.commitArray has less than 64K bits, the extra
|
---|
292 | // bits of 'c' are used as a check to make sure that the
|
---|
293 | // signing operation is not using an out of range count value
|
---|
294 | t1 = (UINT16)currentCount;
|
---|
295 |
|
---|
296 | // If the lower bits of c are greater or equal to the lower bits of t1
|
---|
297 | // then the upper bits of t1 must be one more than the upper bits
|
---|
298 | // of c
|
---|
299 | if((*c & COMMIT_INDEX_MASK) >= (t1 & COMMIT_INDEX_MASK))
|
---|
300 | // Since the counter is behind, reduce the current count
|
---|
301 | currentCount = currentCount - (COMMIT_INDEX_MASK + 1);
|
---|
302 |
|
---|
303 | t1 = (UINT16)currentCount;
|
---|
304 | if((t1 & ~COMMIT_INDEX_MASK) != (*c & ~COMMIT_INDEX_MASK))
|
---|
305 | return FALSE;
|
---|
306 | // set the counter to the value that was
|
---|
307 | // present when the commitment was made
|
---|
308 | currentCount = (currentCount & 0xffffffffffff0000ULL) | *c; /* libtpms changed */
|
---|
309 | }
|
---|
310 | // Marshal the count value to a TPM2B buffer for the KDF
|
---|
311 | cntr.t.size = sizeof(currentCount);
|
---|
312 | UINT64_TO_BYTE_ARRAY(currentCount, cntr.t.buffer);
|
---|
313 |
|
---|
314 | // Now can do the KDF to create the random value for the signing operation
|
---|
315 | // During the creation process, we may generate an r that does not meet the
|
---|
316 | // requirements of the random value.
|
---|
317 | // want to generate a new r.
|
---|
318 | r->t.size = n.t.size;
|
---|
319 |
|
---|
320 | for(iterations = 1; iterations < 1000000;)
|
---|
321 | {
|
---|
322 | int i;
|
---|
323 | CryptKDFa(CONTEXT_INTEGRITY_HASH_ALG,
|
---|
324 | &gr.commitNonce.b,
|
---|
325 | COMMIT_STRING,
|
---|
326 | (TPM2B *)name, // libtpms changed
|
---|
327 | &cntr.b,
|
---|
328 | n.t.size * 8,
|
---|
329 | r->t.buffer,
|
---|
330 | &iterations,
|
---|
331 | FALSE);
|
---|
332 |
|
---|
333 | // "random" value must be less than the prime
|
---|
334 | if(UnsignedCompareB(r->b.size, r->b.buffer, n.t.size, n.t.buffer) >= 0)
|
---|
335 | continue;
|
---|
336 |
|
---|
337 | // in this implementation it is required that at least bit
|
---|
338 | // in the upper half of the number be set
|
---|
339 | for(i = n.t.size / 2; i >= 0; i--)
|
---|
340 | if(r->b.buffer[i] != 0)
|
---|
341 | return TRUE;
|
---|
342 | }
|
---|
343 | return FALSE;
|
---|
344 | }
|
---|
345 |
|
---|
346 | //*** CryptCommit()
|
---|
347 | // This function is called when the count value is committed. The 'gr.commitArray'
|
---|
348 | // value associated with the current count value is SET and g_commitCounter is
|
---|
349 | // incremented. The low-order 16 bits of old value of the counter is returned.
|
---|
350 | UINT16
|
---|
351 | CryptCommit(void)
|
---|
352 | {
|
---|
353 | UINT16 oldCount = (UINT16)gr.commitCounter;
|
---|
354 | gr.commitCounter++;
|
---|
355 | SET_BIT(oldCount & COMMIT_INDEX_MASK, gr.commitArray);
|
---|
356 | return oldCount;
|
---|
357 | }
|
---|
358 |
|
---|
359 | //*** CryptEndCommit()
|
---|
360 | // This function is called when the signing operation using the committed value
|
---|
361 | // is completed. It clears the gr.commitArray bit associated with the count
|
---|
362 | // value so that it can't be used again.
|
---|
363 | void CryptEndCommit(UINT16 c // IN: the counter value of the commitment
|
---|
364 | )
|
---|
365 | {
|
---|
366 | ClearBit((c & COMMIT_INDEX_MASK), gr.commitArray, sizeof(gr.commitArray));
|
---|
367 | }
|
---|
368 |
|
---|
369 | //*** CryptEccGetParameters()
|
---|
370 | // This function returns the ECC parameter details of the given curve.
|
---|
371 | // Return Type: BOOL
|
---|
372 | // TRUE(1) success
|
---|
373 | // FALSE(0) unsupported ECC curve ID
|
---|
374 | BOOL CryptEccGetParameters(
|
---|
375 | TPM_ECC_CURVE curveId, // IN: ECC curve ID
|
---|
376 | TPMS_ALGORITHM_DETAIL_ECC* parameters // OUT: ECC parameters
|
---|
377 | )
|
---|
378 | {
|
---|
379 | const TPM_ECC_CURVE_METADATA* curve = CryptEccGetParametersByCurveId(curveId);
|
---|
380 | BOOL found = curve != NULL;
|
---|
381 |
|
---|
382 | if(found)
|
---|
383 | {
|
---|
384 | parameters->curveID = curve->curveId;
|
---|
385 | parameters->keySize = curve->keySizeBits;
|
---|
386 | parameters->kdf = curve->kdf;
|
---|
387 | parameters->sign = curve->sign;
|
---|
388 | // BnTo2B(data->prime, ¶meters->p.b, 0);
|
---|
389 | found = found
|
---|
390 | && TpmMath_IntTo2B(ExtEcc_CurveGetPrime(curveId),
|
---|
391 | ¶meters->p.b,
|
---|
392 | parameters->p.t.size);
|
---|
393 | found = found
|
---|
394 | && TpmMath_IntTo2B(ExtEcc_CurveGet_a(curveId), ¶meters->a.b,
|
---|
395 | parameters->p.t.size /* libtpms changed for HLK */);
|
---|
396 | found = found
|
---|
397 | && TpmMath_IntTo2B(ExtEcc_CurveGet_b(curveId), ¶meters->b.b,
|
---|
398 | parameters->p.t.size /* libtpms changed for HLK */);
|
---|
399 | found = found
|
---|
400 | && TpmMath_IntTo2B(ExtEcc_CurveGetGx(curveId),
|
---|
401 | ¶meters->gX.b,
|
---|
402 | parameters->p.t.size);
|
---|
403 | found = found
|
---|
404 | && TpmMath_IntTo2B(ExtEcc_CurveGetGy(curveId),
|
---|
405 | ¶meters->gY.b,
|
---|
406 | parameters->p.t.size);
|
---|
407 | // BnTo2B(data->base.x, ¶meters->gX.b, 0);
|
---|
408 | // BnTo2B(data->base.y, ¶meters->gY.b, 0);
|
---|
409 | found =
|
---|
410 | found
|
---|
411 | && TpmMath_IntTo2B(ExtEcc_CurveGetOrder(curveId), ¶meters->n.b, 0);
|
---|
412 | found =
|
---|
413 | found
|
---|
414 | && TpmMath_IntTo2B(ExtEcc_CurveGetCofactor(curveId), ¶meters->h.b, 0);
|
---|
415 | // if we got into this IF but failed to get a parameter from the external
|
---|
416 | // library, our crypto systems are broken; enter failure mode.
|
---|
417 | if(!found)
|
---|
418 | {
|
---|
419 | FAIL(FATAL_ERROR_MATHLIBRARY);
|
---|
420 | }
|
---|
421 | }
|
---|
422 | return found;
|
---|
423 | }
|
---|
424 |
|
---|
425 | //*** TpmEcc_IsValidPrivateEcc()
|
---|
426 | // Checks that 0 < 'x' < 'q'
|
---|
427 | BOOL TpmEcc_IsValidPrivateEcc(const Crypt_Int* x, // IN: private key to check
|
---|
428 | const Crypt_EccCurve* E // IN: the curve to check
|
---|
429 | )
|
---|
430 | {
|
---|
431 | BOOL retVal;
|
---|
432 | retVal =
|
---|
433 | (!ExtMath_IsZero(x)
|
---|
434 | && (ExtMath_UnsignedCmp(x, ExtEcc_CurveGetOrder(ExtEcc_CurveGetCurveId(E)))
|
---|
435 | < 0));
|
---|
436 | return retVal;
|
---|
437 | }
|
---|
438 |
|
---|
439 | LIB_EXPORT BOOL CryptEccIsValidPrivateKey(TPM2B_ECC_PARAMETER* d,
|
---|
440 | TPM_ECC_CURVE curveId)
|
---|
441 | {
|
---|
442 | CRYPT_INT_INITIALIZED(bnD, MAX_ECC_PARAMETER_BYTES * 8, d);
|
---|
443 | return !ExtMath_IsZero(bnD)
|
---|
444 | && (ExtMath_UnsignedCmp(bnD, ExtEcc_CurveGetOrder(curveId)) < 0);
|
---|
445 | }
|
---|
446 |
|
---|
447 | //*** TpmEcc_PointMult()
|
---|
448 | // This function does a point multiply of the form 'R' = ['d']'S' + ['u']'Q' where the
|
---|
449 | // parameters are Crypt_Int* values. If 'S' is NULL and d is not NULL, then it computes
|
---|
450 | // 'R' = ['d']'G' + ['u']'Q' or just 'R' = ['d']'G' if 'u' and 'Q' are NULL.
|
---|
451 | // If 'skipChecks' is TRUE, then the function will not verify that the inputs are
|
---|
452 | // correct for the domain. This would be the case when the values were created by the
|
---|
453 | // CryptoEngine code.
|
---|
454 | // It will return TPM_RC_NO_RESULT if the resulting point is the point at infinity.
|
---|
455 | // Return Type: TPM_RC
|
---|
456 | // TPM_RC_NO_RESULT result of multiplication is a point at infinity
|
---|
457 | // TPM_RC_ECC_POINT 'S' or 'Q' is not on the curve
|
---|
458 | // TPM_RC_VALUE 'd' or 'u' is not < n
|
---|
459 | TPM_RC
|
---|
460 | TpmEcc_PointMult(Crypt_Point* R, // OUT: computed point
|
---|
461 | const Crypt_Point* S, // IN: optional point to multiply by 'd'
|
---|
462 | const Crypt_Int* d, // IN: scalar for [d]S or [d]G
|
---|
463 | const Crypt_Point* Q, // IN: optional second point
|
---|
464 | const Crypt_Int* u, // IN: optional second scalar
|
---|
465 | const Crypt_EccCurve* E // IN: curve parameters
|
---|
466 | )
|
---|
467 | {
|
---|
468 | BOOL OK;
|
---|
469 | //
|
---|
470 | TPM_DO_SELF_TEST(TPM_ALG_ECDH);
|
---|
471 |
|
---|
472 | // Need one scalar
|
---|
473 | OK = (d != NULL || u != NULL);
|
---|
474 |
|
---|
475 | // If S is present, then d has to be present. If S is not
|
---|
476 | // present, then d may or may not be present
|
---|
477 | OK = OK && (((S == NULL) == (d == NULL)) || (d != NULL));
|
---|
478 |
|
---|
479 | // either both u and Q have to be provided or neither can be provided (don't
|
---|
480 | // know what to do if only one is provided.
|
---|
481 | OK = OK && ((u == NULL) == (Q == NULL));
|
---|
482 |
|
---|
483 | OK = OK && (E != NULL);
|
---|
484 | if(!OK)
|
---|
485 | return TPM_RC_VALUE;
|
---|
486 |
|
---|
487 | OK = (S == NULL) || ExtEcc_IsPointOnCurve(S, E);
|
---|
488 | OK = OK && ((Q == NULL) || ExtEcc_IsPointOnCurve(Q, E));
|
---|
489 | if(!OK)
|
---|
490 | return TPM_RC_ECC_POINT;
|
---|
491 |
|
---|
492 | if((d != NULL) && (S == NULL))
|
---|
493 | S = ExtEcc_CurveGetG(ExtEcc_CurveGetCurveId(E));
|
---|
494 | // If only one scalar, don't need Shamir's trick
|
---|
495 | if((d == NULL) || (u == NULL))
|
---|
496 | {
|
---|
497 | if(d == NULL)
|
---|
498 | OK = ExtEcc_PointMultiply(R, Q, u, E);
|
---|
499 | else
|
---|
500 | OK = ExtEcc_PointMultiply(R, S, d, E);
|
---|
501 | }
|
---|
502 | else
|
---|
503 | {
|
---|
504 | OK = ExtEcc_PointMultiplyAndAdd(R, S, d, Q, u, E);
|
---|
505 | }
|
---|
506 | return (OK ? TPM_RC_SUCCESS : TPM_RC_NO_RESULT);
|
---|
507 | }
|
---|
508 |
|
---|
509 | //***TpmEcc_GenPrivateScalar()
|
---|
510 | // This function gets random values that are the size of the key plus 64 bits. The
|
---|
511 | // value is reduced (mod ('q' - 1)) and incremented by 1 ('q' is the order of the
|
---|
512 | // curve. This produces a value ('d') such that 1 <= 'd' < 'q'. This is the method
|
---|
513 | // of FIPS 186-4 Section B.4.1 ""Key Pair Generation Using Extra Random Bits"".
|
---|
514 | // Return Type: BOOL
|
---|
515 | // TRUE(1) success
|
---|
516 | // FALSE(0) failure generating private key
|
---|
517 | #if !USE_OPENSSL_FUNCTIONS_EC // libtpms added
|
---|
518 | BOOL TpmEcc_GenPrivateScalar(
|
---|
519 | Crypt_Int* dOut, // OUT: the qualified random value
|
---|
520 | const Crypt_EccCurve* E, // IN: curve for which the private key
|
---|
521 | // needs to be appropriate
|
---|
522 | RAND_STATE* rand // IN: state for DRBG
|
---|
523 | )
|
---|
524 | {
|
---|
525 | TPM_ECC_CURVE curveId = ExtEcc_CurveGetCurveId(E);
|
---|
526 | const Crypt_Int* order = ExtEcc_CurveGetOrder(curveId);
|
---|
527 | BOOL OK;
|
---|
528 | UINT32 orderBits = ExtMath_SizeInBits(order);
|
---|
529 | UINT32 orderBytes = BITS_TO_BYTES(orderBits);
|
---|
530 | CRYPT_INT_VAR(bnExtraBits, MAX_ECC_KEY_BITS + 64);
|
---|
531 | CRYPT_INT_VAR(nMinus1, MAX_ECC_KEY_BITS);
|
---|
532 | //
|
---|
533 | OK = TpmMath_GetRandomInteger(bnExtraBits, (orderBytes * 8) + 64, rand);
|
---|
534 | OK = OK && ExtMath_SubtractWord(nMinus1, order, 1);
|
---|
535 | OK = OK && ExtMath_Mod(bnExtraBits, nMinus1);
|
---|
536 | OK = OK && ExtMath_AddWord(dOut, bnExtraBits, 1);
|
---|
537 | return OK && !g_inFailureMode;
|
---|
538 | }
|
---|
539 | #else // libtpms added begin
|
---|
540 | BOOL TpmEcc_GenPrivateScalar(
|
---|
541 | Crypt_Int* dOut, // OUT: the qualified random value
|
---|
542 | const Crypt_EccCurve* E, // IN: curve for which the private key
|
---|
543 | // needs to be appropriate
|
---|
544 | const EC_GROUP* G, // IN: the EC_GROUP to use; must be != NULL for rand == NULL
|
---|
545 | BOOL noLeadingZeros, // IN: require that all bytes in the private key be set
|
---|
546 | // result may not have leading zero bytes
|
---|
547 | RAND_STATE* rand // IN: state for DRBG
|
---|
548 | )
|
---|
549 | {
|
---|
550 | TPM_ECC_CURVE curveId = ExtEcc_CurveGetCurveId(E);
|
---|
551 | const Crypt_Int* order = ExtEcc_CurveGetOrder(curveId);
|
---|
552 | BOOL OK;
|
---|
553 | UINT32 orderBits = ExtMath_SizeInBits(order);
|
---|
554 | UINT32 orderBytes = BITS_TO_BYTES(orderBits);
|
---|
555 | UINT32 requestedBits = 0;
|
---|
556 | CRYPT_INT_VAR(bnExtraBits, MAX_ECC_KEY_BITS + 64);
|
---|
557 | CRYPT_INT_VAR(nMinus1, MAX_ECC_KEY_BITS);
|
---|
558 |
|
---|
559 | if (rand == NULL) {
|
---|
560 | if (noLeadingZeros)
|
---|
561 | requestedBits = orderBits;
|
---|
562 |
|
---|
563 | return OpenSSLEccGetPrivate((bigNum)dOut, G, requestedBits);
|
---|
564 | }
|
---|
565 |
|
---|
566 | //
|
---|
567 | OK = TpmMath_GetRandomInteger(bnExtraBits, (orderBytes * 8) + 64, rand);
|
---|
568 | OK = OK && ExtMath_SubtractWord(nMinus1, order, 1);
|
---|
569 | OK = OK && ExtMath_Mod(bnExtraBits, nMinus1);
|
---|
570 | OK = OK && ExtMath_AddWord(dOut, bnExtraBits, 1);
|
---|
571 | return OK && !g_inFailureMode;
|
---|
572 | }
|
---|
573 | #endif // USE_OPENSSL_FUNCTIONS_EC libtpms added end
|
---|
574 |
|
---|
575 | #if !USE_OPENSSL_FUNCTIONS_EC // libtpms added
|
---|
576 | //*** TpmEcc_GenerateKeyPair()
|
---|
577 | // This function gets a private scalar from the source of random bits and does
|
---|
578 | // the point multiply to get the public key.
|
---|
579 | BOOL TpmEcc_GenerateKeyPair(Crypt_Int* bnD, // OUT: private scalar
|
---|
580 | Crypt_Point* ecQ, // OUT: public point
|
---|
581 | const Crypt_EccCurve* E, // IN: curve for the point
|
---|
582 | RAND_STATE* rand // IN: DRBG state to use
|
---|
583 | )
|
---|
584 | {
|
---|
585 | BOOL OK = FALSE;
|
---|
586 | // Get a private scalar
|
---|
587 | OK = TpmEcc_GenPrivateScalar(bnD, E, rand);
|
---|
588 |
|
---|
589 | // Do a point multiply
|
---|
590 | OK = OK && ExtEcc_PointMultiply(ecQ, NULL, bnD, E);
|
---|
591 | return OK;
|
---|
592 | }
|
---|
593 |
|
---|
594 | #else // libtpms added begin
|
---|
595 |
|
---|
596 | /* In this version of BnEccGenerateKeyPair we take a dual approach to constant
|
---|
597 | time requirements: For curves whose order is at the byte boundary, e.g.
|
---|
598 | NIST P224/P256/P384, we make sure that bnD has all bytes set (no leading zeros)
|
---|
599 | so that OpenSSL BIGNUM code will not reduce the number of bytes and the
|
---|
600 | subsequent BnEccModMult() would run faster for a shoter value. For all other
|
---|
601 | curves whose order is not at the byte boundary, e.g. NIST P521, we simply
|
---|
602 | always add the order of the curve to bnD and call BnEccModMult() with the
|
---|
603 | result bnD1, which leads to the same result. */
|
---|
604 | BOOL TpmEcc_GenerateKeyPair(Crypt_Int* bnD, // OUT: private scalar
|
---|
605 | Crypt_Point* ecQ, // OUT: public point
|
---|
606 | const Crypt_EccCurve* E, // IN: curve for the point
|
---|
607 | RAND_STATE* rand // IN: DRBG state to use
|
---|
608 | )
|
---|
609 | {
|
---|
610 | BOOL OK = FALSE;
|
---|
611 | TPM_ECC_CURVE curveId = ExtEcc_CurveGetCurveId(E);
|
---|
612 | const Crypt_Int* order = ExtEcc_CurveGetOrder(curveId);
|
---|
613 | UINT32 orderBits = ExtMath_SizeInBits(order);
|
---|
614 | BOOL atByteBoundary = (orderBits & 7) == 0;
|
---|
615 | BOOL noLeadingZeros = atByteBoundary;
|
---|
616 | CRYPT_ECC_NUM(bnD1);
|
---|
617 |
|
---|
618 | // We request that bnD not have leading zeros if it is at byte-boundary,
|
---|
619 | // like for example it is the case for NIST P256.
|
---|
620 | OK = TpmEcc_GenPrivateScalar(bnD, E, E->G, noLeadingZeros, rand);
|
---|
621 | if (!atByteBoundary) {
|
---|
622 | // for NIST P521 we can add the order to bnD to ensure we have
|
---|
623 | // a constant amount of bytes; the result is the same as if we
|
---|
624 | // were doing the BnEccModMult() calculation with bnD.
|
---|
625 | OK = OK && ExtMath_Add(bnD1, bnD, order);
|
---|
626 | OK = OK && ExtEcc_PointMultiply(ecQ, NULL, bnD1, E);
|
---|
627 | } else {
|
---|
628 | OK = OK && ExtEcc_PointMultiply(ecQ, NULL, bnD, E);
|
---|
629 | }
|
---|
630 | return OK;
|
---|
631 | }
|
---|
632 |
|
---|
633 | #endif // libtpms added end
|
---|
634 |
|
---|
635 | //***CryptEccNewKeyPair(***)
|
---|
636 | // This function creates an ephemeral ECC. It is ephemeral in that
|
---|
637 | // is expected that the private part of the key will be discarded
|
---|
638 | LIB_EXPORT TPM_RC CryptEccNewKeyPair(
|
---|
639 | TPMS_ECC_POINT* Qout, // OUT: the public point
|
---|
640 | TPM2B_ECC_PARAMETER* dOut, // OUT: the private scalar
|
---|
641 | TPM_ECC_CURVE curveId // IN: the curve for the key
|
---|
642 | )
|
---|
643 | {
|
---|
644 | CRYPT_CURVE_INITIALIZED(E, curveId);
|
---|
645 | CRYPT_POINT_VAR(ecQ);
|
---|
646 | CRYPT_ECC_NUM(bnD);
|
---|
647 | BOOL OK;
|
---|
648 |
|
---|
649 | if(E == NULL)
|
---|
650 | return TPM_RC_CURVE;
|
---|
651 |
|
---|
652 | TPM_DO_SELF_TEST(TPM_ALG_ECDH);
|
---|
653 | OK = TpmEcc_GenerateKeyPair(bnD, ecQ, E, NULL);
|
---|
654 | if(OK)
|
---|
655 | {
|
---|
656 | TpmEcc_PointTo2B(Qout, ecQ, E);
|
---|
657 | TpmMath_IntTo2B(bnD, &dOut->b, Qout->x.t.size);
|
---|
658 | }
|
---|
659 | else
|
---|
660 | {
|
---|
661 | Qout->x.t.size = Qout->y.t.size = dOut->t.size = 0;
|
---|
662 | }
|
---|
663 | CRYPT_CURVE_FREE(E);
|
---|
664 | return OK ? TPM_RC_SUCCESS : TPM_RC_NO_RESULT;
|
---|
665 | }
|
---|
666 |
|
---|
667 | //*** CryptEccPointMultiply()
|
---|
668 | // This function computes 'R' := ['dIn']'G' + ['uIn']'QIn'. Where 'dIn' and
|
---|
669 | // 'uIn' are scalars, 'G' and 'QIn' are points on the specified curve and 'G' is the
|
---|
670 | // default generator of the curve.
|
---|
671 | //
|
---|
672 | // The 'xOut' and 'yOut' parameters are optional and may be set to NULL if not
|
---|
673 | // used.
|
---|
674 | //
|
---|
675 | // It is not necessary to provide 'uIn' if 'QIn' is specified but one of 'uIn' and
|
---|
676 | // 'dIn' must be provided. If 'dIn' and 'QIn' are specified but 'uIn' is not
|
---|
677 | // provided, then 'R' = ['dIn']'QIn'.
|
---|
678 | //
|
---|
679 | // If the multiply produces the point at infinity, the TPM_RC_NO_RESULT is returned.
|
---|
680 | //
|
---|
681 | // The sizes of 'xOut' and yOut' will be set to be the size of the degree of
|
---|
682 | // the curve
|
---|
683 | //
|
---|
684 | // It is a fatal error if 'dIn' and 'uIn' are both unspecified (NULL) or if 'Qin'
|
---|
685 | // or 'Rout' is unspecified.
|
---|
686 | //
|
---|
687 | // Return Type: TPM_RC
|
---|
688 | // TPM_RC_ECC_POINT the point 'Pin' or 'Qin' is not on the curve
|
---|
689 | // TPM_RC_NO_RESULT the product point is at infinity
|
---|
690 | // TPM_RC_CURVE bad curve
|
---|
691 | // TPM_RC_VALUE 'dIn' or 'uIn' out of range
|
---|
692 | //
|
---|
693 | LIB_EXPORT TPM_RC CryptEccPointMultiply(
|
---|
694 | TPMS_ECC_POINT* Rout, // OUT: the product point R
|
---|
695 | TPM_ECC_CURVE curveId, // IN: the curve to use
|
---|
696 | TPMS_ECC_POINT* Pin, // IN: first point (can be null)
|
---|
697 | TPM2B_ECC_PARAMETER* dIn, // IN: scalar value for [dIn]Qin
|
---|
698 | // the Pin
|
---|
699 | TPMS_ECC_POINT* Qin, // IN: point Q
|
---|
700 | TPM2B_ECC_PARAMETER* uIn // IN: scalar value for the multiplier
|
---|
701 | // of Q
|
---|
702 | )
|
---|
703 | {
|
---|
704 | CRYPT_CURVE_INITIALIZED(E, curveId);
|
---|
705 | CRYPT_POINT_INITIALIZED(ecP, Pin);
|
---|
706 | CRYPT_ECC_INITIALIZED(bnD, dIn); // If dIn is null, then bnD is null
|
---|
707 | CRYPT_ECC_INITIALIZED(bnU, uIn);
|
---|
708 | CRYPT_POINT_INITIALIZED(ecQ, Qin);
|
---|
709 | CRYPT_POINT_VAR(ecR);
|
---|
710 | TPM_RC retVal;
|
---|
711 | //
|
---|
712 | retVal = TpmEcc_PointMult(ecR, ecP, bnD, ecQ, bnU, E);
|
---|
713 |
|
---|
714 | if(retVal == TPM_RC_SUCCESS)
|
---|
715 | TpmEcc_PointTo2B(Rout, ecR, E);
|
---|
716 | else
|
---|
717 | ClearPoint2B(Rout);
|
---|
718 | CRYPT_CURVE_FREE(E);
|
---|
719 | return retVal;
|
---|
720 | }
|
---|
721 |
|
---|
722 | //*** CryptEccIsPointOnCurve()
|
---|
723 | // This function is used to test if a point is on a defined curve. It does this
|
---|
724 | // by checking that 'y'^2 mod 'p' = 'x'^3 + 'a'*'x' + 'b' mod 'p'.
|
---|
725 | //
|
---|
726 | // It is a fatal error if 'Q' is not specified (is NULL).
|
---|
727 | // Return Type: BOOL
|
---|
728 | // TRUE(1) point is on curve
|
---|
729 | // FALSE(0) point is not on curve or curve is not supported
|
---|
730 | LIB_EXPORT BOOL CryptEccIsPointOnCurve(
|
---|
731 | TPM_ECC_CURVE curveId, // IN: the curve selector
|
---|
732 | TPMS_ECC_POINT* Qin // IN: the point.
|
---|
733 | )
|
---|
734 | {
|
---|
735 | CRYPT_CURVE_INITIALIZED(E, curveId);
|
---|
736 | CRYPT_POINT_INITIALIZED(ecQ, Qin);
|
---|
737 | BOOL OK;
|
---|
738 | //
|
---|
739 | pAssert(Qin != NULL);
|
---|
740 | OK = (E != NULL && (ExtEcc_IsPointOnCurve(ecQ, E)));
|
---|
741 | CRYPT_CURVE_FREE(E); // libtpms added
|
---|
742 | return OK;
|
---|
743 | }
|
---|
744 |
|
---|
745 | //*** CryptEccGenerateKey()
|
---|
746 | // This function generates an ECC key pair based on the input parameters.
|
---|
747 | // This routine uses KDFa to produce candidate numbers. The method is according
|
---|
748 | // to FIPS 186-3, section B.1.2 "Key Pair Generation by Testing Candidates."
|
---|
749 | // According to the method in FIPS 186-3, the resulting private value 'd' should be
|
---|
750 | // 1 <= 'd' < 'n' where 'n' is the order of the base point.
|
---|
751 | //
|
---|
752 | // It is a fatal error if 'Qout', 'dOut', is not provided (is NULL).
|
---|
753 | //
|
---|
754 | // If the curve is not supported
|
---|
755 | // If 'seed' is not provided, then a random number will be used for the key
|
---|
756 | // Return Type: TPM_RC
|
---|
757 | // TPM_RC_CURVE curve is not supported
|
---|
758 | // TPM_RC_NO_RESULT could not verify key with signature (FIPS only)
|
---|
759 | LIB_EXPORT TPM_RC CryptEccGenerateKey(
|
---|
760 | TPMT_PUBLIC* publicArea, // IN/OUT: The public area template for
|
---|
761 | // the new key. The public key
|
---|
762 | // area will be replaced computed
|
---|
763 | // ECC public key
|
---|
764 | TPMT_SENSITIVE* sensitive, // OUT: the sensitive area will be
|
---|
765 | // updated to contain the private
|
---|
766 | // ECC key and the symmetric
|
---|
767 | // encryption key
|
---|
768 | RAND_STATE* rand // IN: if not NULL, the deterministic
|
---|
769 | // RNG state
|
---|
770 | )
|
---|
771 | {
|
---|
772 | CRYPT_CURVE_INITIALIZED(E, publicArea->parameters.eccDetail.curveID);
|
---|
773 | CRYPT_ECC_NUM(bnD);
|
---|
774 | CRYPT_POINT_VAR(ecQ);
|
---|
775 | BOOL OK;
|
---|
776 | TPM_RC retVal;
|
---|
777 | //
|
---|
778 | TPM_DO_SELF_TEST(TPM_ALG_ECDSA); // ECDSA is used to verify each key
|
---|
779 |
|
---|
780 | // Validate parameters
|
---|
781 | if(E == NULL)
|
---|
782 | ERROR_EXIT(TPM_RC_CURVE);
|
---|
783 |
|
---|
784 | publicArea->unique.ecc.x.t.size = 0;
|
---|
785 | publicArea->unique.ecc.y.t.size = 0;
|
---|
786 | sensitive->sensitive.ecc.t.size = 0;
|
---|
787 |
|
---|
788 | OK = TpmEcc_GenerateKeyPair(bnD, ecQ, E, rand);
|
---|
789 | if(OK)
|
---|
790 | {
|
---|
791 | TpmEcc_PointTo2B(&publicArea->unique.ecc, ecQ, E);
|
---|
792 | TpmMath_IntTo2B(
|
---|
793 | bnD, &sensitive->sensitive.ecc.b, publicArea->unique.ecc.x.t.size);
|
---|
794 | }
|
---|
795 | //# if FIPS_COMPLIANT // libtpms changed
|
---|
796 | // See if PWCT is required
|
---|
797 | if(OK && IS_ATTRIBUTE(publicArea->objectAttributes, TPMA_OBJECT, sign) && // libtpms changed begin
|
---|
798 | RuntimeProfileRequiresAttributeFlags(&g_RuntimeProfile,
|
---|
799 | RUNTIME_ATTRIBUTE_PAIRWISE_CONSISTENCY_TEST)) // libtpms changed end
|
---|
800 | {
|
---|
801 | CRYPT_ECC_NUM(bnT);
|
---|
802 | CRYPT_ECC_NUM(bnS);
|
---|
803 | TPM2B_DIGEST digest;
|
---|
804 | //
|
---|
805 | TPM_DO_SELF_TEST(TPM_ALG_ECDSA);
|
---|
806 | digest.t.size = MIN(sensitive->sensitive.ecc.t.size, sizeof(digest.t.buffer));
|
---|
807 | // Get a random value to sign using the built in DRBG state
|
---|
808 | DRBG_Generate(NULL, digest.t.buffer, digest.t.size);
|
---|
809 | if(g_inFailureMode)
|
---|
810 | return TPM_RC_FAILURE;
|
---|
811 | TpmEcc_SignEcdsa(bnT, bnS, E, bnD, &digest, NULL);
|
---|
812 | // and make sure that we can validate the signature
|
---|
813 | OK = TpmEcc_ValidateSignatureEcdsa(bnT, bnS, E, ecQ, &digest)
|
---|
814 | == TPM_RC_SUCCESS;
|
---|
815 | }
|
---|
816 | //# endif // libtpms changed
|
---|
817 | retVal = (OK) ? TPM_RC_SUCCESS : TPM_RC_NO_RESULT;
|
---|
818 | Exit:
|
---|
819 | CRYPT_CURVE_FREE(E);
|
---|
820 | return retVal;
|
---|
821 | }
|
---|
822 |
|
---|
823 | // libtpms added begin
|
---|
824 | // Support for some curves may be compiled in but they may not be
|
---|
825 | // supported by openssl's crypto library.
|
---|
826 | LIB_EXPORT BOOL
|
---|
827 | CryptEccIsCurveRuntimeUsable(
|
---|
828 | TPMI_ECC_CURVE curveId
|
---|
829 | )
|
---|
830 | {
|
---|
831 | CRYPT_CURVE_INITIALIZED(E, curveId);
|
---|
832 | if (E == NULL)
|
---|
833 | return FALSE;
|
---|
834 | CRYPT_CURVE_FREE(E);
|
---|
835 | return TRUE;
|
---|
836 | }
|
---|
837 | // libtpms added end
|
---|
838 |
|
---|
839 | #endif // ALG_ECC
|
---|