1 /*
2  * Copyright (C) 2023 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 "pbkdf2_openssl.h"
17 
18 #include <gtest/gtest.h>
19 #include "securec.h"
20 
21 #include "detailed_pbkdf2_params.h"
22 #include "kdf.h"
23 #include "log.h"
24 #include "memory.h"
25 
26 using namespace std;
27 using namespace testing::ext;
28 
29 namespace {
30 class CryptoPbkdf2Test : public testing::Test {
31 public:
32     static void SetUpTestCase();
33     static void TearDownTestCase();
34     void SetUp();
35     void TearDown();
36 };
37 
SetUpTestCase()38 void CryptoPbkdf2Test::SetUpTestCase() {}
TearDownTestCase()39 void CryptoPbkdf2Test::TearDownTestCase() {}
40 
SetUp()41 void CryptoPbkdf2Test::SetUp() // add init here, this will be called before test.
42 {
43 }
44 
TearDown()45 void CryptoPbkdf2Test::TearDown() // add destroy here, this will be called when test case done.
46 {
47 }
48 
49 static const char *g_pbkdf2Name = "PBKDF2";
50 static const char *g_errorName = "abcd";
51 static const char *g_password = "123456";
52 static const char *g_passwordEmpty = "";
53 static const char *g_passwordLong = "12345678123456781234567812345678123456781234567812345678123456781234567812345678";
54 
55 constexpr uint32_t OUT_PUT_MAX_LENGTH = 128;
56 constexpr uint32_t OUT_PUT_NORMAL_LENGTH = 32;
57 constexpr uint32_t SALT_NORMAL_LENGTH = 16;
58 
59 HWTEST_F(CryptoPbkdf2Test, CryptoPbkdf2Test1, TestSize.Level0)
60 {
61     HcfKdf *generator = nullptr;
62     HcfResult ret = HcfKdfCreate("PBKDF2|SHA256", &generator);
63     EXPECT_EQ(ret, HCF_SUCCESS);
64     uint8_t out[OUT_PUT_MAX_LENGTH] = {0};
65     uint8_t saltData[SALT_NORMAL_LENGTH] = {0};
66     HcfBlob output = {.data = out, .len = OUT_PUT_NORMAL_LENGTH};
67     HcfBlob salt = {.data = saltData, .len = SALT_NORMAL_LENGTH};
68     HcfBlob password = {.data = reinterpret_cast<uint8_t *>(const_cast<char *>(g_password)),
69         .len = strlen(g_password)};
70     HcfPBKDF2ParamsSpec params = {
71         .base.algName = g_pbkdf2Name,
72         .password = password,
73         .salt = salt,
74         .iterations = 10000,
75         .output = output,
76     };
77     ret = generator->generateSecret(generator, &(params.base));
78     EXPECT_EQ(ret, HCF_SUCCESS);
79     HcfObjDestroy(generator);
80 }
81 
82 HWTEST_F(CryptoPbkdf2Test, CryptoPbkdf2ErrTest1, TestSize.Level0)
83 {
84     HcfKdf *generator = nullptr;
85     HcfResult ret = HcfKdfCreate("PBKDF2|SHA256", &generator);
86     EXPECT_EQ(ret, HCF_SUCCESS);
87     uint8_t out[OUT_PUT_MAX_LENGTH] = {0};
88     uint8_t saltData[SALT_NORMAL_LENGTH] = {0};
89     HcfBlob output = {.data = out, .len = OUT_PUT_NORMAL_LENGTH};
90     HcfBlob salt = {.data = saltData, .len = SALT_NORMAL_LENGTH};
91     HcfBlob password = {.data = reinterpret_cast<uint8_t *>(const_cast<char *>(g_password)),
92         .len = strlen(g_password)};
93     HcfPBKDF2ParamsSpec params = {
94         .base.algName = g_pbkdf2Name,
95         .password = password,
96         .salt = salt,
97         .iterations = 10000,
98         .output = output,
99     };
100     generator->base.destroy(nullptr);
101     ret = generator->generateSecret(nullptr, &(params.base));
102     EXPECT_EQ(ret, HCF_INVALID_PARAMS);
103     HcfObjDestroy(generator);
104 }
105 
106 HWTEST_F(CryptoPbkdf2Test, CryptoPbkdf2Test2, TestSize.Level0)
107 {
108     HcfKdf *generator = nullptr;
109     HcfResult ret = HcfKdfCreate("PBKDF2|SHA256", &generator);
110     EXPECT_EQ(ret, HCF_SUCCESS);
111     uint8_t out[OUT_PUT_MAX_LENGTH] = {0};
112     HcfBlob output = {.data = out, .len = OUT_PUT_NORMAL_LENGTH};
113     HcfBlob salt = {.data = nullptr, .len = 0};
114     HcfBlob password = {.data = nullptr, .len = 0};
115     HcfPBKDF2ParamsSpec params = {
116         .base.algName = g_pbkdf2Name,
117         .password = password,
118         .salt = salt,
119         .iterations = 10000,
120         .output = output,
121     };
122     ret = generator->generateSecret(generator, &(params.base));
123     EXPECT_EQ(ret, HCF_SUCCESS);
124     HcfObjDestroy(generator);
125 }
126 
127 HWTEST_F(CryptoPbkdf2Test, CryptoPbkdf2Test3, TestSize.Level0)
128 {
129     HcfKdf *generator = nullptr;
130     HcfResult ret = HcfKdfCreate("PBKDF2|SHA256", &generator);
131     EXPECT_EQ(ret, HCF_SUCCESS);
132     uint8_t out[OUT_PUT_MAX_LENGTH] = {0};
133     HcfBlob output = {.data = out, .len = OUT_PUT_NORMAL_LENGTH};
134     HcfBlob salt = {.data = nullptr, .len = 0};
135     HcfBlob password = {.data = reinterpret_cast<uint8_t *>(const_cast<char *>(g_passwordEmpty)),
136     .len = strlen(g_passwordEmpty)};
137     HcfPBKDF2ParamsSpec params = {
138         .base.algName = g_pbkdf2Name,
139         .password = password,
140         .salt = salt,
141         .iterations = 10000,
142         .output = output,
143     };
144     ret = generator->generateSecret(generator, &(params.base));
145     EXPECT_EQ(ret, HCF_SUCCESS);
146     HcfObjDestroy(generator);
147 }
148 
149 HWTEST_F(CryptoPbkdf2Test, CryptoPbkdf2Test4, TestSize.Level0)
150 {
151     // long password (long than md length)
152     HcfKdf *generator = nullptr;
153     HcfResult ret = HcfKdfCreate("PBKDF2|SHA256", &generator);
154     EXPECT_EQ(ret, HCF_SUCCESS);
155     uint8_t out[OUT_PUT_MAX_LENGTH] = {0};
156     HcfBlob output = {.data = out, .len = OUT_PUT_NORMAL_LENGTH};
157     uint8_t saltData[SALT_NORMAL_LENGTH] = {0};
158     HcfBlob salt = {.data = saltData, .len = SALT_NORMAL_LENGTH};
159     HcfBlob password = {.data = reinterpret_cast<uint8_t *>(const_cast<char *>(g_passwordLong)),
160     .len = strlen(g_passwordLong)};
161     HcfPBKDF2ParamsSpec params = {
162         .base.algName = g_pbkdf2Name,
163         .password = password,
164         .salt = salt,
165         .iterations = 10000,
166         .output = output,
167     };
168     ret = generator->generateSecret(generator, &(params.base));
169     EXPECT_EQ(ret, HCF_SUCCESS);
170     HcfObjDestroy(generator);
171 }
172 
173 HWTEST_F(CryptoPbkdf2Test, CryptoPbkdf2Test5, TestSize.Level0)
174 {
175     // password not empty but zero length
176     HcfKdf *generator = nullptr;
177     HcfResult ret = HcfKdfCreate("PBKDF2|SHA256", &generator);
178     EXPECT_EQ(ret, HCF_SUCCESS);
179     uint8_t out[OUT_PUT_MAX_LENGTH] = {0};
180     uint8_t saltData[SALT_NORMAL_LENGTH] = {0};
181     HcfBlob output = {.data = out, .len = OUT_PUT_NORMAL_LENGTH};
182     HcfBlob salt = {.data = saltData, .len = SALT_NORMAL_LENGTH};
183     HcfBlob password = {.data = reinterpret_cast<uint8_t *>(const_cast<char *>(g_passwordLong)),
184     .len = 0};
185     HcfPBKDF2ParamsSpec params = {
186         .base.algName = g_pbkdf2Name,
187         .password = password,
188         .salt = salt,
189         .iterations = 10000,
190         .output = output,
191     };
192     ret = generator->generateSecret(generator, &(params.base));
193     EXPECT_EQ(ret, HCF_SUCCESS);
194     HcfObjDestroy(generator);
195 }
196 
197 HWTEST_F(CryptoPbkdf2Test, CryptoPbkdf2Test6, TestSize.Level0)
198 {
199     HcfKdf *generator = nullptr;
200     HcfResult ret = HcfKdfCreate("PBKDF2|SHA1", &generator);
201     EXPECT_EQ(ret, HCF_SUCCESS);
202     uint8_t out[OUT_PUT_MAX_LENGTH] = {0};
203     uint8_t saltData[SALT_NORMAL_LENGTH] = {0};
204     HcfBlob output = {.data = out, .len = OUT_PUT_NORMAL_LENGTH};
205     HcfBlob salt = {.data = saltData, .len = SALT_NORMAL_LENGTH};
206     HcfBlob password = {.data = reinterpret_cast<uint8_t *>(const_cast<char *>(g_password)),
207         .len = strlen(g_password)};
208     HcfPBKDF2ParamsSpec params = {
209         .base.algName = g_pbkdf2Name,
210         .password = password,
211         .salt = salt,
212         .iterations = 10000,
213         .output = output,
214     };
215     ret = generator->generateSecret(generator, &(params.base));
216     EXPECT_EQ(ret, HCF_SUCCESS);
217     HcfObjDestroy(generator);
218 }
219 
220 HWTEST_F(CryptoPbkdf2Test, CryptoPbkdf2Test7, TestSize.Level0)
221 {
222     HcfKdf *generator = nullptr;
223     HcfResult ret = HcfKdfCreate("PBKDF2|SHA384", &generator);
224     EXPECT_EQ(ret, HCF_SUCCESS);
225     uint8_t out[OUT_PUT_MAX_LENGTH] = {0};
226     uint8_t saltData[SALT_NORMAL_LENGTH] = {0};
227     HcfBlob output = {.data = out, .len = OUT_PUT_NORMAL_LENGTH};
228     HcfBlob salt = {.data = saltData, .len = SALT_NORMAL_LENGTH};
229     HcfBlob password = {.data = reinterpret_cast<uint8_t *>(const_cast<char *>(g_password)),
230         .len = strlen(g_password)};
231     HcfPBKDF2ParamsSpec params = {
232         .base.algName = g_pbkdf2Name,
233         .password = password,
234         .salt = salt,
235         .iterations = 10000,
236         .output = output,
237     };
238     ret = generator->generateSecret(generator, &(params.base));
239     EXPECT_EQ(ret, HCF_SUCCESS);
240     HcfObjDestroy(generator);
241 }
242 
243 HWTEST_F(CryptoPbkdf2Test, CryptoPbkdf2Test8, TestSize.Level0)
244 {
245     HcfKdf *generator = nullptr;
246     HcfResult ret = HcfKdfCreate("PBKDF2|SHA512", &generator);
247     EXPECT_EQ(ret, HCF_SUCCESS);
248     uint8_t out[OUT_PUT_MAX_LENGTH] = {0};
249     uint8_t saltData[SALT_NORMAL_LENGTH] = {0};
250     HcfBlob output = {.data = out, .len = OUT_PUT_NORMAL_LENGTH};
251     HcfBlob salt = {.data = saltData, .len = SALT_NORMAL_LENGTH};
252     HcfBlob password = {.data = reinterpret_cast<uint8_t *>(const_cast<char *>(g_password)),
253         .len = strlen(g_password)};
254     HcfPBKDF2ParamsSpec params = {
255         .base.algName = g_pbkdf2Name,
256         .password = password,
257         .salt = salt,
258         .iterations = 10000,
259         .output = output,
260     };
261     ret = generator->generateSecret(generator, &(params.base));
262     EXPECT_EQ(ret, HCF_SUCCESS);
263     HcfObjDestroy(generator);
264 }
265 
266 HWTEST_F(CryptoPbkdf2Test, CryptoPbkdf2Test9, TestSize.Level0)
267 {
268     HcfKdf *generator = nullptr;
269     HcfResult ret = HcfKdfCreate("PBKDF2|SM3", &generator);
270     EXPECT_EQ(ret, HCF_SUCCESS);
271     uint8_t out[OUT_PUT_MAX_LENGTH] = {0};
272     uint8_t saltData[SALT_NORMAL_LENGTH] = {0};
273     HcfBlob output = {.data = out, .len = OUT_PUT_NORMAL_LENGTH};
274     HcfBlob salt = {.data = saltData, .len = SALT_NORMAL_LENGTH};
275     HcfBlob password = {.data = reinterpret_cast<uint8_t *>(const_cast<char *>(g_password)),
276         .len = strlen(g_password)};
277     HcfPBKDF2ParamsSpec params = {
278         .base.algName = g_pbkdf2Name,
279         .password = password,
280         .salt = salt,
281         .iterations = 10000,
282         .output = output,
283     };
284     ret = generator->generateSecret(generator, &(params.base));
285     EXPECT_EQ(ret, HCF_SUCCESS);
286     HcfObjDestroy(generator);
287 }
288 
289 HWTEST_F(CryptoPbkdf2Test, CryptoPbkdf2Test10, TestSize.Level0)
290 {
291     HcfKdf *generator = nullptr;
292     HcfResult ret = HcfKdfCreate("PBKDF2|SHA224", &generator);
293     EXPECT_EQ(ret, HCF_SUCCESS);
294     uint8_t out[OUT_PUT_MAX_LENGTH] = {0};
295     uint8_t saltData[SALT_NORMAL_LENGTH] = {0};
296     HcfBlob output = {.data = out, .len = OUT_PUT_NORMAL_LENGTH};
297     HcfBlob salt = {.data = saltData, .len = SALT_NORMAL_LENGTH};
298     HcfBlob password = {.data = reinterpret_cast<uint8_t *>(const_cast<char *>(g_password)),
299         .len = strlen(g_password)};
300     HcfPBKDF2ParamsSpec params = {
301         .base.algName = g_pbkdf2Name,
302         .password = password,
303         .salt = salt,
304         .iterations = 10000,
305         .output = output,
306     };
307     ret = generator->generateSecret(generator, &(params.base));
308     EXPECT_EQ(ret, HCF_SUCCESS);
309     HcfObjDestroy(generator);
310 }
311 
312 HWTEST_F(CryptoPbkdf2Test, CryptoPbkdf2TestError1, TestSize.Level1)
313 {
314     // params iter = 0
315     HcfKdf *generator = nullptr;
316     HcfResult ret = HcfKdfCreate("PBKDF2|SHA256", &generator);
317     EXPECT_EQ(ret, HCF_SUCCESS);
318     uint8_t out[OUT_PUT_MAX_LENGTH] = {0};
319     HcfBlob output = {.data = out, .len = OUT_PUT_NORMAL_LENGTH};
320     HcfBlob salt = {.data = nullptr, .len = 0};
321     HcfPBKDF2ParamsSpec params = {
322         .base.algName = const_cast<char *>(g_pbkdf2Name),
323         .password = {.data = nullptr, .len = 0},
324         .salt = salt,
325         .iterations = 0,
326         .output = output,
327     };
328     ret = generator->generateSecret(generator, &(params.base));
329     EXPECT_NE(ret, HCF_SUCCESS);
330     HcfObjDestroy(generator);
331 }
332 
333 HWTEST_F(CryptoPbkdf2Test, CryptoPbkdf2TestError2, TestSize.Level1)
334 {
335     // params algName is error
336     HcfKdf *generator = nullptr;
337     HcfResult ret = HcfKdfCreate("PBKDF2|SHA256", &generator);
338     EXPECT_EQ(ret, HCF_SUCCESS);
339     uint8_t out[OUT_PUT_MAX_LENGTH] = {0};
340     HcfBlob output = {.data = out, .len = OUT_PUT_NORMAL_LENGTH};
341     HcfBlob salt = {.data = nullptr, .len = 0};
342     HcfBlob password = {.data = nullptr, .len = 0};
343     HcfPBKDF2ParamsSpec params = {
344         .base.algName = const_cast<char *>(g_errorName),
345         .password = password,
346         .salt = salt,
347         .iterations = 10000,
348         .output = output,
349     };
350     ret = generator->generateSecret(generator, &(params.base));
351     EXPECT_NE(ret, HCF_SUCCESS);
352     HcfObjDestroy(generator);
353 }
354 
355 HWTEST_F(CryptoPbkdf2Test, CryptoPbkdf2TestError3, TestSize.Level1)
356 {
357     // params algName is nullptr
358     HcfKdf *generator = nullptr;
359     HcfResult ret = HcfKdfCreate("PBKDF2|SHA256", &generator);
360     EXPECT_EQ(ret, HCF_SUCCESS);
361     uint8_t out[OUT_PUT_MAX_LENGTH] = {0};
362     HcfBlob output = {.data = out, .len = OUT_PUT_NORMAL_LENGTH};
363     HcfBlob salt = {.data = nullptr, .len = 0};
364     HcfPBKDF2ParamsSpec params = {
365         .base.algName = nullptr,
366         .password = {.data = nullptr, .len = 0},
367         .salt = salt,
368         .iterations = 10000,
369         .output = output,
370     };
371     ret = generator->generateSecret(generator, &(params.base));
372     EXPECT_NE(ret, HCF_SUCCESS);
373     HcfObjDestroy(generator);
374 }
375 
376 HWTEST_F(CryptoPbkdf2Test, CryptoPbkdf2TestError4, TestSize.Level1)
377 {
378     // output len is 0 and data is nullptr
379     HcfKdf *generator = nullptr;
380     HcfResult ret = HcfKdfCreate("PBKDF2|SHA256", &generator);
381     EXPECT_EQ(ret, HCF_SUCCESS);
382     uint8_t saltData[SALT_NORMAL_LENGTH] = {0};
383     HcfBlob output = {.data = nullptr, .len = 0};
384     HcfBlob salt = {.data = saltData, .len = SALT_NORMAL_LENGTH};
385     HcfPBKDF2ParamsSpec params = {
386         .base.algName = g_pbkdf2Name,
387         .password = {.data = nullptr, .len = 0},
388         .salt = salt,
389         .iterations = 10000,
390         .output = output,
391     };
392     ret = generator->generateSecret(generator, &(params.base));
393     EXPECT_NE(ret, HCF_SUCCESS);
394     HcfObjDestroy(generator);
395 }
396 
397 HWTEST_F(CryptoPbkdf2Test, CryptoPbkdf2TestError5, TestSize.Level1)
398 {
399     // output data is nullptr
400     HcfKdf *generator = nullptr;
401     HcfResult ret = HcfKdfCreate("PBKDF2|SHA256", &generator);
402     EXPECT_EQ(ret, HCF_SUCCESS);
403     uint8_t saltData[SALT_NORMAL_LENGTH] = {0};
404     HcfBlob output = {.data = nullptr, .len = OUT_PUT_NORMAL_LENGTH};
405     HcfBlob salt = {.data = saltData, .len = SALT_NORMAL_LENGTH};
406     HcfPBKDF2ParamsSpec params = {
407         .base.algName = g_pbkdf2Name,
408         .password = {.data = nullptr, .len = 0},
409         .salt = salt,
410         .iterations = 10000,
411         .output = output,
412     };
413     ret = generator->generateSecret(generator, &(params.base));
414     EXPECT_NE(ret, HCF_SUCCESS);
415     HcfObjDestroy(generator);
416 }
417 
418 HWTEST_F(CryptoPbkdf2Test, CryptoPbkdf2TestError6, TestSize.Level1)
419 {
420     // output len is 0
421     HcfKdf *generator = nullptr;
422     HcfResult ret = HcfKdfCreate("PBKDF2|SHA256", &generator);
423     EXPECT_EQ(ret, HCF_SUCCESS);
424     uint8_t saltData[SALT_NORMAL_LENGTH] = {0};
425     uint8_t out[OUT_PUT_MAX_LENGTH] = {1, 1};
426     HcfBlob output = {.data = out, .len = 0};
427     HcfBlob salt = {.data = saltData, .len = SALT_NORMAL_LENGTH};
428     HcfPBKDF2ParamsSpec params = {
429         .base.algName = g_pbkdf2Name,
430         .password = {.data = nullptr, .len = 0},
431         .salt = salt,
432         .iterations = 10000,
433         .output = output,
434     };
435     ret = generator->generateSecret(generator, &(params.base));
436     EXPECT_NE(ret, HCF_SUCCESS);
437     HcfObjDestroy(generator);
438 }
439 
440 HWTEST_F(CryptoPbkdf2Test, CryptoPbkdf2TestError7, TestSize.Level1)
441 {
442     // use basic params
443     HcfKdf *generator = nullptr;
444     HcfResult ret = HcfKdfCreate("PBKDF2|SHA256", &generator);
445     EXPECT_EQ(ret, HCF_SUCCESS);
446     HcfKdfParamsSpec params = {
447         .algName = g_pbkdf2Name,
448     };
449     ret = generator->generateSecret(generator, &params);
450     EXPECT_NE(ret, HCF_SUCCESS);
451     HcfObjDestroy(generator);
452 }
453 
454 HWTEST_F(CryptoPbkdf2Test, CryptoPbkdf2TestError8, TestSize.Level1)
455 {
456     // use nullptr params
457     HcfKdf *generator = nullptr;
458     HcfResult ret = HcfKdfCreate("PBKDF2|SHA256", &generator);
459     EXPECT_EQ(ret, HCF_SUCCESS);
460     ret = generator->generateSecret(generator, nullptr);
461     EXPECT_NE(ret, HCF_SUCCESS);
462     HcfObjDestroy(generator);
463 }
464 
465 HWTEST_F(CryptoPbkdf2Test, CryptoPbkdf2TestError9, TestSize.Level1)
466 {
467     HcfKdf *generator = nullptr;
468     HcfResult ret = HcfKdfCreate("PBKDF2|abcd", &generator);
469     EXPECT_NE(ret, HCF_SUCCESS);
470     HcfObjDestroy(generator);
471 }
472 
473 HWTEST_F(CryptoPbkdf2Test, CryptoPbkdf2TestError10, TestSize.Level1)
474 {
475     HcfKdf *generator = nullptr;
476     HcfResult ret = HcfKdfCreate("ABCD|SM3", &generator);
477     EXPECT_NE(ret, HCF_SUCCESS);
478     HcfObjDestroy(generator);
479 }
480 
481 HWTEST_F(CryptoPbkdf2Test, CryptoPbkdf2TestError11, TestSize.Level1)
482 {
483     HcfKdf *generator = nullptr;
484     HcfResult ret = HcfKdfCreate(nullptr, &generator);
485     EXPECT_NE(ret, HCF_SUCCESS);
486     HcfObjDestroy(generator);
487 }
488 
489 HWTEST_F(CryptoPbkdf2Test, CryptoPbkdf2TestError12, TestSize.Level1)
490 {
491     HcfResult ret = HcfKdfCreate(nullptr, nullptr);
492     EXPECT_NE(ret, HCF_SUCCESS);
493 }
494 
495 HWTEST_F(CryptoPbkdf2Test, CryptoPbkdf2TestError13, TestSize.Level0)
496 {
497     HcfResult ret = HcfKdfPBKDF2SpiCreate(nullptr, nullptr);
498     EXPECT_EQ(ret, HCF_INVALID_PARAMS);
499 
500     HcfKdfDeriveParams params = {};
501     params.algo = HCF_ALG_PKBDF2;
502     params.md = HCF_OPENSSL_DIGEST_SHA256;
503 
504     HcfKdfSpi *spiObj = nullptr;
505     ret = HcfKdfPBKDF2SpiCreate(&params, &spiObj);
506     EXPECT_EQ(ret, HCF_SUCCESS);
507 
508     (void)spiObj->base.destroy(nullptr);
509     ret = spiObj->generateSecret(nullptr, nullptr);
510     EXPECT_EQ(ret, HCF_INVALID_PARAMS);
511 
512     HcfObjDestroy(spiObj);
513 }
514 }
515