• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *    http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "crypto_signature.h"
17 #include "sm2_ec_signature_data.h"
18 #include "securec.h"
19 #include <string.h>
20 #include <stdlib.h>
21 #include "signature.h"
22 #include "memory.h"
23 #include "crypto_common.h"
24 #include "blob.h"
25 #include "object_base.h"
26 #include "result.h"
27 #include "native_common.h"
28 
29 struct OH_CryptoVerify {
30     HcfObjectBase base;
31 
32     HcfResult (*init)(HcfVerify *self, HcfParamsSpec *params, HcfPubKey *publicKey);
33 
34     HcfResult (*update)(HcfVerify *self, HcfBlob *data);
35 
36     bool (*verify)(HcfVerify *self, HcfBlob *data, HcfBlob *signatureData);
37 
38     HcfResult (*recover)(HcfVerify *self, HcfBlob *signatureData, HcfBlob *rawSignatureData);
39 
40     const char *(*getAlgoName)(HcfVerify *self);
41 
42     HcfResult (*setVerifySpecInt)(HcfVerify *self, SignSpecItem item, int32_t saltLen);
43 
44     HcfResult (*getVerifySpecString)(HcfVerify *self, SignSpecItem item, char **returnString);
45 
46     HcfResult (*getVerifySpecInt)(HcfVerify *self, SignSpecItem item, int32_t *returnInt);
47 
48     HcfResult (*setVerifySpecUint8Array)(HcfVerify *self, SignSpecItem item, HcfBlob blob);
49 };
50 
51 struct OH_CryptoSign {
52     HcfObjectBase base;
53 
54     HcfResult (*init)(HcfSign *self, HcfParamsSpec *params, HcfPriKey *privateKey);
55 
56     HcfResult (*update)(HcfSign *self, HcfBlob *data);
57 
58     HcfResult (*sign)(HcfSign *self, HcfBlob *data, HcfBlob *returnSignatureData);
59 
60     const char *(*getAlgoName)(HcfSign *self);
61 
62     HcfResult (*setSignSpecInt)(HcfSign *self, SignSpecItem item, int32_t saltLen);
63 
64     HcfResult (*getSignSpecString)(HcfSign *self, SignSpecItem item, char **returnString);
65 
66     HcfResult (*getSignSpecInt)(HcfSign *self, SignSpecItem item, int32_t *returnInt);
67 
68     HcfResult (*setSignSpecUint8Array)(HcfSign *self, SignSpecItem item, HcfBlob blob);
69 };
70 
OH_CryptoVerify_Create(const char * algoName,OH_CryptoVerify ** verify)71 OH_Crypto_ErrCode OH_CryptoVerify_Create(const char *algoName, OH_CryptoVerify **verify)
72 {
73     if (verify == NULL) {
74         return CRYPTO_INVALID_PARAMS;
75     }
76     HcfResult ret = HcfVerifyCreate(algoName, (HcfVerify **)verify);
77     return GetOhCryptoErrCode(ret);
78 }
79 
OH_CryptoVerify_Init(OH_CryptoVerify * ctx,OH_CryptoPubKey * pubKey)80 OH_Crypto_ErrCode OH_CryptoVerify_Init(OH_CryptoVerify *ctx, OH_CryptoPubKey *pubKey)
81 {
82     if ((ctx == NULL) || (ctx->init == NULL) || (pubKey == NULL)) {
83         return CRYPTO_INVALID_PARAMS;
84     }
85     HcfResult ret = ctx->init((HcfVerify *)ctx, NULL, (HcfPubKey *)pubKey);
86     return GetOhCryptoErrCode(ret);
87 }
88 
OH_CryptoVerify_Update(OH_CryptoVerify * ctx,Crypto_DataBlob * in)89 OH_Crypto_ErrCode OH_CryptoVerify_Update(OH_CryptoVerify *ctx, Crypto_DataBlob *in)
90 {
91     if ((ctx == NULL) || (ctx->update == NULL) || (in == NULL)) {
92         return CRYPTO_INVALID_PARAMS;
93     }
94     HcfResult ret = ctx->update((HcfVerify *)ctx, (HcfBlob *)in);
95     return GetOhCryptoErrCode(ret);
96 }
97 
OH_CryptoVerify_Final(OH_CryptoVerify * ctx,Crypto_DataBlob * in,Crypto_DataBlob * signData)98 bool OH_CryptoVerify_Final(OH_CryptoVerify *ctx, Crypto_DataBlob *in, Crypto_DataBlob *signData)
99 {
100     if ((ctx == NULL) || (ctx->verify == NULL) || (signData == NULL)) {
101         return false;
102     }
103     bool ret = ctx->verify((HcfVerify *)ctx, (HcfBlob *)in, (HcfBlob *)signData);
104     if (ret != true) {
105         return false;
106     }
107 
108     return ret;
109 }
110 
OH_CryptoVerify_Recover(OH_CryptoVerify * ctx,Crypto_DataBlob * signData,Crypto_DataBlob * rawSignData)111 OH_Crypto_ErrCode OH_CryptoVerify_Recover(OH_CryptoVerify *ctx, Crypto_DataBlob *signData,
112     Crypto_DataBlob *rawSignData)
113 {
114     if ((ctx == NULL) || (ctx->recover == NULL) || (signData == NULL) || (rawSignData == NULL)) {
115         return CRYPTO_INVALID_PARAMS;
116     }
117     HcfResult ret = ctx->recover((HcfVerify *)ctx, (HcfBlob *)signData, (HcfBlob *)rawSignData);
118     return GetOhCryptoErrCode(ret);
119 }
120 
OH_CryptoVerify_GetAlgoName(OH_CryptoVerify * ctx)121 const char *OH_CryptoVerify_GetAlgoName(OH_CryptoVerify *ctx)
122 {
123     if ((ctx == NULL) || (ctx->getAlgoName == NULL)) {
124         return NULL;
125     }
126     return ctx->getAlgoName((HcfVerify *)ctx);
127 }
128 
OH_CryptoVerify_SetParam(OH_CryptoVerify * ctx,CryptoSignature_ParamType type,Crypto_DataBlob * value)129 OH_Crypto_ErrCode OH_CryptoVerify_SetParam(OH_CryptoVerify *ctx, CryptoSignature_ParamType type,
130     Crypto_DataBlob *value)
131 {
132     if ((ctx == NULL) || (value == NULL)) {
133         return CRYPTO_INVALID_PARAMS;
134     }
135     HcfResult ret = HCF_INVALID_PARAMS;
136     switch (type) {
137         case CRYPTO_PSS_SALT_LEN_INT:
138         case CRYPTO_PSS_TRAILER_FIELD_INT:
139             if ((value->data == NULL) || (value->len != sizeof(int32_t)) || (ctx->setVerifySpecInt == NULL)) {
140                 ret = HCF_INVALID_PARAMS;
141                 break;
142             }
143             ret = ctx->setVerifySpecInt((HcfVerify *)ctx, (SignSpecItem)type, *((int32_t *)value->data));
144             break;
145         case CRYPTO_SM2_USER_ID_DATABLOB:
146         case CRYPTO_PSS_MGF1_NAME_STR:
147         case CRYPTO_PSS_MGF_NAME_STR:
148         case CRYPTO_PSS_MD_NAME_STR:
149             if (ctx->setVerifySpecUint8Array == NULL) {
150                 ret = HCF_INVALID_PARAMS;
151                 break;
152             }
153             ret = ctx->setVerifySpecUint8Array((HcfVerify *)ctx, (SignSpecItem)type, *((HcfBlob *)value));
154             break;
155         default:
156             return CRYPTO_INVALID_PARAMS;
157     }
158     return GetOhCryptoErrCode(ret);
159 }
160 
OH_CryptoVerify_GetParam(OH_CryptoVerify * ctx,CryptoSignature_ParamType type,Crypto_DataBlob * value)161 OH_Crypto_ErrCode OH_CryptoVerify_GetParam(OH_CryptoVerify *ctx, CryptoSignature_ParamType type,
162     Crypto_DataBlob *value)
163 {
164     if ((ctx == NULL) || (value == NULL)) {
165         return CRYPTO_INVALID_PARAMS;
166     }
167     int32_t *returnInt = NULL;
168     char *returnStr = NULL;
169     HcfResult ret = HCF_INVALID_PARAMS;
170     switch (type) {
171         case CRYPTO_PSS_SALT_LEN_INT:
172         case CRYPTO_PSS_TRAILER_FIELD_INT:
173         case CRYPTO_SM2_USER_ID_DATABLOB:
174             if (ctx->getVerifySpecInt == NULL) {
175                 ret = HCF_INVALID_PARAMS;
176                 break;
177             }
178             returnInt = (int32_t *)HcfMalloc(sizeof(int32_t), 0);
179             if (returnInt == NULL) {
180                 return CRYPTO_MEMORY_ERROR;
181             }
182             ret = ctx->getVerifySpecInt((HcfVerify *)ctx, (SignSpecItem)type, returnInt);
183             if (ret != HCF_SUCCESS) {
184                 HcfFree(returnInt);
185                 returnInt = NULL;
186                 break;
187             }
188             value->data = (uint8_t *)returnInt;
189             value->len = sizeof(int32_t);
190             break;
191         case CRYPTO_PSS_MD_NAME_STR:
192         case CRYPTO_PSS_MGF_NAME_STR:
193         case CRYPTO_PSS_MGF1_NAME_STR:
194             if (ctx->getVerifySpecString == NULL) {
195                 ret = HCF_INVALID_PARAMS;
196                 break;
197             }
198             ret = ctx->getVerifySpecString((HcfVerify *)ctx, (SignSpecItem)type, &returnStr);
199             if (ret != HCF_SUCCESS) {
200                 break;
201             }
202             value->data = (uint8_t *)returnStr;
203             value->len = strlen(returnStr);
204             break;
205         default:
206             return CRYPTO_INVALID_PARAMS;
207     }
208     return GetOhCryptoErrCode(ret);
209 }
210 
211 
OH_CryptoVerify_Destroy(OH_CryptoVerify * ctx)212 void OH_CryptoVerify_Destroy(OH_CryptoVerify *ctx)
213 {
214     if (ctx == NULL || ctx->base.destroy == NULL) {
215         return;
216     }
217     ctx->base.destroy((HcfObjectBase *)ctx);
218 }
219 
OH_CryptoSign_Create(const char * algoName,OH_CryptoSign ** sign)220 OH_Crypto_ErrCode OH_CryptoSign_Create(const char *algoName, OH_CryptoSign **sign)
221 {
222     if (sign == NULL) {
223         return CRYPTO_PARAMETER_CHECK_FAILED;
224     }
225     HcfResult ret = HcfSignCreate(algoName, (HcfSign **)sign);
226     return GetOhCryptoErrCodeNew(ret);
227 }
228 
OH_CryptoSign_Init(OH_CryptoSign * ctx,OH_CryptoPrivKey * privKey)229 OH_Crypto_ErrCode OH_CryptoSign_Init(OH_CryptoSign *ctx, OH_CryptoPrivKey *privKey)
230 {
231     if ((ctx == NULL) || (ctx->init == NULL) || (privKey == NULL)) {
232         return CRYPTO_PARAMETER_CHECK_FAILED;
233     }
234     HcfResult ret = ctx->init((HcfSign *)ctx, NULL, (HcfPriKey *)privKey);
235     return GetOhCryptoErrCodeNew(ret);
236 }
237 
OH_CryptoSign_Update(OH_CryptoSign * ctx,const Crypto_DataBlob * in)238 OH_Crypto_ErrCode OH_CryptoSign_Update(OH_CryptoSign *ctx, const Crypto_DataBlob *in)
239 {
240     if ((ctx == NULL) || (ctx->update == NULL) || (in == NULL)) {
241         return CRYPTO_PARAMETER_CHECK_FAILED;
242     }
243     HcfResult ret = ctx->update((HcfSign *)ctx, (HcfBlob *)in);
244     return GetOhCryptoErrCodeNew(ret);
245 }
246 
OH_CryptoSign_Final(OH_CryptoSign * ctx,const Crypto_DataBlob * in,Crypto_DataBlob * out)247 OH_Crypto_ErrCode OH_CryptoSign_Final(OH_CryptoSign *ctx, const Crypto_DataBlob *in, Crypto_DataBlob *out)
248 {
249     if ((ctx == NULL) || (ctx->sign == NULL) || (out == NULL)) {
250         return CRYPTO_PARAMETER_CHECK_FAILED;
251     }
252     HcfResult ret = ctx->sign((HcfSign *)ctx, (HcfBlob *)in, (HcfBlob *)out);
253     return GetOhCryptoErrCodeNew(ret);
254 }
255 
OH_CryptoSign_GetAlgoName(OH_CryptoSign * ctx)256 const char *OH_CryptoSign_GetAlgoName(OH_CryptoSign *ctx)
257 {
258     if ((ctx == NULL) || (ctx->getAlgoName == NULL)) {
259         return NULL;
260     }
261     return ctx->getAlgoName((HcfSign *)ctx);
262 }
263 
OH_CryptoSign_SetParam(OH_CryptoSign * ctx,CryptoSignature_ParamType type,const Crypto_DataBlob * value)264 OH_Crypto_ErrCode OH_CryptoSign_SetParam(OH_CryptoSign *ctx, CryptoSignature_ParamType type,
265     const Crypto_DataBlob *value)
266 {
267     if ((ctx == NULL) || (value == NULL)) {
268         return CRYPTO_PARAMETER_CHECK_FAILED;
269     }
270     HcfResult ret = HCF_INVALID_PARAMS;
271     switch (type) {
272         case CRYPTO_PSS_SALT_LEN_INT:
273         case CRYPTO_PSS_TRAILER_FIELD_INT:
274             if ((value->data == NULL) || (value->len != sizeof(int32_t)) || (ctx->setSignSpecInt == NULL)) {
275                 ret = HCF_INVALID_PARAMS;
276                 break;
277             }
278             ret = ctx->setSignSpecInt((HcfSign *)ctx, (SignSpecItem)type, *((int32_t *)value->data));
279             break;
280         case CRYPTO_SM2_USER_ID_DATABLOB:
281         case CRYPTO_PSS_MGF1_NAME_STR:
282         case CRYPTO_PSS_MGF_NAME_STR:
283         case CRYPTO_PSS_MD_NAME_STR:
284             if (ctx->setSignSpecUint8Array == NULL) {
285                 ret = HCF_INVALID_PARAMS;
286                 break;
287             }
288             ret = ctx->setSignSpecUint8Array((HcfSign *)ctx, (SignSpecItem)type, *((HcfBlob *)value));
289             break;
290         default:
291             return CRYPTO_PARAMETER_CHECK_FAILED;
292     }
293     return GetOhCryptoErrCodeNew(ret);
294 }
295 
OH_CryptoSign_GetParam(OH_CryptoSign * ctx,CryptoSignature_ParamType type,Crypto_DataBlob * value)296 OH_Crypto_ErrCode OH_CryptoSign_GetParam(OH_CryptoSign *ctx, CryptoSignature_ParamType type, Crypto_DataBlob *value)
297 {
298     if ((ctx == NULL) || (value == NULL)) {
299         return CRYPTO_PARAMETER_CHECK_FAILED;
300     }
301     int32_t *returnInt = NULL;
302     char *returnStr = NULL;
303     HcfResult ret = HCF_INVALID_PARAMS;
304     switch (type) {
305         case CRYPTO_PSS_SALT_LEN_INT:
306         case CRYPTO_PSS_TRAILER_FIELD_INT:
307         case CRYPTO_SM2_USER_ID_DATABLOB:
308             if (ctx->getSignSpecInt == NULL) {
309                 ret = HCF_INVALID_PARAMS;
310                 break;
311             }
312             returnInt = (int32_t *)HcfMalloc(sizeof(int32_t), 0);
313             if (returnInt == NULL) {
314                 return CRYPTO_MEMORY_ERROR;
315             }
316             ret = ctx->getSignSpecInt((HcfSign *)ctx, (SignSpecItem)type, returnInt);
317             if (ret != HCF_SUCCESS) {
318                 HcfFree(returnInt);
319                 returnInt = NULL;
320                 break;
321             }
322             value->data = (uint8_t *)returnInt;
323             value->len = sizeof(int32_t);
324             break;
325         case CRYPTO_PSS_MD_NAME_STR:
326         case CRYPTO_PSS_MGF_NAME_STR:
327         case CRYPTO_PSS_MGF1_NAME_STR:
328             if (ctx->getSignSpecString == NULL) {
329                 ret = HCF_INVALID_PARAMS;
330                 break;
331             }
332             ret = ctx->getSignSpecString((HcfSign *)ctx, (SignSpecItem)type, &returnStr);
333             if (ret != HCF_SUCCESS) {
334                 break;
335             }
336             value->data = (uint8_t *)returnStr;
337             value->len = strlen(returnStr);
338             break;
339         default:
340             return CRYPTO_PARAMETER_CHECK_FAILED;
341     }
342     return GetOhCryptoErrCodeNew(ret);
343 }
344 
OH_CryptoSign_Destroy(OH_CryptoSign * ctx)345 void OH_CryptoSign_Destroy(OH_CryptoSign *ctx)
346 {
347     if (ctx == NULL || ctx->base.destroy == NULL) {
348         return;
349     }
350     ctx->base.destroy((HcfObjectBase *)ctx);
351 }
352 
353 struct OH_CryptoEccSignatureSpec {
354     HcfBigInteger r;
355     HcfBigInteger s;
356 };
357 
358 
OH_CryptoEccSignatureSpec_Create(Crypto_DataBlob * eccSignature,OH_CryptoEccSignatureSpec ** spec)359 OH_Crypto_ErrCode OH_CryptoEccSignatureSpec_Create(Crypto_DataBlob *eccSignature, OH_CryptoEccSignatureSpec **spec)
360 {
361     if (spec == NULL) {
362         return CRYPTO_PARAMETER_CHECK_FAILED;
363     }
364     HcfResult ret = HCF_INVALID_PARAMS;
365     if (eccSignature == NULL) {
366         *spec = (OH_CryptoEccSignatureSpec *)HcfMalloc(sizeof(OH_CryptoEccSignatureSpec), 0);
367         if (*spec == NULL) {
368             return CRYPTO_MEMORY_ERROR;
369         }
370         return GetOhCryptoErrCodeNew(HCF_SUCCESS);
371     }
372     ret = HcfGenEcSignatureSpecByData((HcfBlob *)eccSignature, (Sm2EcSignatureDataSpec **)spec);
373     return GetOhCryptoErrCodeNew(ret);
374 }
375 
OH_CryptoEccSignatureSpec_GetRAndS(OH_CryptoEccSignatureSpec * spec,Crypto_DataBlob * r,Crypto_DataBlob * s)376 OH_Crypto_ErrCode OH_CryptoEccSignatureSpec_GetRAndS(OH_CryptoEccSignatureSpec *spec, Crypto_DataBlob *r,
377     Crypto_DataBlob *s)
378 {
379     if ((spec == NULL) || (r == NULL) || (s == NULL)) {
380         return CRYPTO_PARAMETER_CHECK_FAILED;
381     }
382     if ((spec->r.data == NULL) || (spec->s.data == NULL)) {
383         return CRYPTO_PARAMETER_CHECK_FAILED;
384     }
385 
386     // Allocate memory for r->data
387     r->data = (uint8_t *)HcfMalloc(spec->r.len, 0);
388     if (r->data == NULL) {
389         return CRYPTO_MEMORY_ERROR;
390     }
391 
392     // Copy data for r
393     if (memcpy_s(r->data, spec->r.len, spec->r.data, spec->r.len) != 0) {
394         HcfFree(r->data);
395         r->data = NULL; // Ensure pointer is null after freeing
396         return CRYPTO_MEMORY_ERROR;
397     }
398     r->len = (size_t)spec->r.len;
399 
400     // Allocate memory for s->data
401     s->data = (uint8_t *)HcfMalloc(spec->s.len, 0);
402     if (s->data == NULL) {
403         HcfFree(r->data);
404         r->data = NULL; // Ensure pointer is null after freeing
405         return CRYPTO_MEMORY_ERROR;
406     }
407 
408     // Copy data for s
409     if (memcpy_s(s->data, spec->s.len, spec->s.data, spec->s.len) != 0) {
410         HcfFree(r->data);
411         r->data = NULL; // Ensure pointer is null after freeing
412         HcfFree(s->data);
413         s->data = NULL; // Ensure pointer is null after freeing
414         return CRYPTO_MEMORY_ERROR;
415     }
416     s->len = (size_t)spec->s.len;
417 
418     return GetOhCryptoErrCodeNew(HCF_SUCCESS);
419 }
420 
OH_CryptoEccSignatureSpec_SetRAndS(OH_CryptoEccSignatureSpec * spec,Crypto_DataBlob * r,Crypto_DataBlob * s)421 OH_Crypto_ErrCode OH_CryptoEccSignatureSpec_SetRAndS(OH_CryptoEccSignatureSpec *spec, Crypto_DataBlob *r,
422     Crypto_DataBlob *s)
423 {
424     if ((spec == NULL) || (r == NULL) || (s == NULL)) {
425         return CRYPTO_PARAMETER_CHECK_FAILED;
426     }
427     if (r->data == NULL || s->data == NULL) {
428         return CRYPTO_PARAMETER_CHECK_FAILED;
429     }
430 
431     // Allocate memory for r->data
432     spec->r.data = (unsigned char *)HcfMalloc(r->len, 0);
433     if (spec->r.data == NULL) {
434         return CRYPTO_MEMORY_ERROR;
435     }
436     if (memcpy_s(spec->r.data, r->len, r->data, r->len) != 0) {
437         HcfFree(spec->r.data);
438         spec->r.data = NULL;
439         return CRYPTO_MEMORY_ERROR;
440     }
441     spec->r.len = (uint32_t)r->len;
442 
443     // Allocate memory for s->data
444     spec->s.data = (unsigned char *)HcfMalloc(s->len, 0);
445     if (spec->s.data == NULL) {
446         HcfFree(spec->r.data);
447         spec->r.data = NULL;
448         return CRYPTO_MEMORY_ERROR;
449     }
450     if (memcpy_s(spec->s.data, s->len, s->data, s->len) != 0) {
451         HcfFree(spec->r.data);
452         HcfFree(spec->s.data);
453         spec->r.data = NULL;
454         spec->s.data = NULL;
455         return CRYPTO_MEMORY_ERROR;
456     }
457     spec->s.len = (uint32_t)s->len;
458 
459     return GetOhCryptoErrCodeNew(HCF_SUCCESS);
460 }
461 
OH_CryptoEccSignatureSpec_Encode(OH_CryptoEccSignatureSpec * spec,Crypto_DataBlob * out)462 OH_Crypto_ErrCode OH_CryptoEccSignatureSpec_Encode(OH_CryptoEccSignatureSpec *spec, Crypto_DataBlob *out)
463 {
464     if ((spec == NULL) || (out == NULL)) {
465         return CRYPTO_PARAMETER_CHECK_FAILED;
466     }
467     HcfBlob *outBlob = (HcfBlob *)HcfMalloc(sizeof(HcfBlob), 0);
468     if (outBlob == NULL) {
469         return CRYPTO_MEMORY_ERROR;
470     }
471 
472     HcfResult ret = HcfGenEcSignatureDataBySpec((Sm2EcSignatureDataSpec *)spec, outBlob);
473     if (ret != HCF_SUCCESS) {
474         HcfFree(outBlob);
475         outBlob = NULL;
476         return GetOhCryptoErrCode(ret);
477     }
478     out->data = outBlob->data;
479     out->len = outBlob->len;
480     HcfFree(outBlob);
481     outBlob = NULL;
482     return GetOhCryptoErrCodeNew(HCF_SUCCESS);
483 }
484 
OH_CryptoEccSignatureSpec_Destroy(OH_CryptoEccSignatureSpec * spec)485 void OH_CryptoEccSignatureSpec_Destroy(OH_CryptoEccSignatureSpec *spec)
486 {
487     if (spec == NULL) {
488         return;
489     }
490     if (spec->r.data != NULL) {
491         HcfFree(spec->r.data);
492         spec->r.data = NULL;
493     }
494     if (spec->s.data != NULL) {
495         HcfFree(spec->s.data);
496         spec->s.data = NULL;
497     }
498     HcfFree(spec);
499 }
500