/src/gnutls/lib/hash_int.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2000-2012 Free Software Foundation, Inc. |
3 | | * |
4 | | * Author: Nikos Mavrogiannopoulos |
5 | | * |
6 | | * This file is part of GnuTLS. |
7 | | * |
8 | | * The GnuTLS is free software; you can redistribute it and/or |
9 | | * modify it under the terms of the GNU Lesser General Public License |
10 | | * as published by the Free Software Foundation; either version 2.1 of |
11 | | * the License, or (at your option) any later version. |
12 | | * |
13 | | * This library is distributed in the hope that it will be useful, but |
14 | | * WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | | * Lesser General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU Lesser General Public License |
19 | | * along with this program. If not, see <https://www.gnu.org/licenses/> |
20 | | * |
21 | | */ |
22 | | |
23 | | /* This file handles all the internal functions that cope with hashes |
24 | | * and HMACs. |
25 | | */ |
26 | | |
27 | | #include "gnutls_int.h" |
28 | | #include "hash_int.h" |
29 | | #include "errors.h" |
30 | | #include "algorithms.h" |
31 | | #include "fips.h" |
32 | | |
33 | | #ifdef ENABLE_PKCS11 |
34 | | #include "pkcs11/p11_provider.h" |
35 | | #endif |
36 | | |
37 | | int _gnutls_hash_init(digest_hd_st *dig, const mac_entry_st *e) |
38 | 3.26M | { |
39 | 3.26M | int result; |
40 | 3.26M | const gnutls_crypto_digest_st *cc = NULL; |
41 | | |
42 | 3.26M | FAIL_IF_LIB_ERROR; |
43 | | |
44 | 3.26M | if (unlikely(e == NULL || e->id == GNUTLS_MAC_NULL)) |
45 | 0 | return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); |
46 | | |
47 | 3.26M | dig->e = e; |
48 | | |
49 | | /* check if a digest has been registered |
50 | | */ |
51 | 3.26M | cc = _gnutls_get_crypto_digest((gnutls_digest_algorithm_t)e->id); |
52 | 3.26M | if ( |
53 | | #if defined(ENABLE_PKCS11) && defined(ENABLE_FIPS140) |
54 | | /* Prioritize crypto from pkcs11 provider */ |
55 | | !_p11_provider_is_initialized() && |
56 | | #endif |
57 | 3.26M | cc != NULL && cc->init) { |
58 | 49.4k | if (cc->init((gnutls_digest_algorithm_t)e->id, &dig->handle) < |
59 | 49.4k | 0) { |
60 | 0 | gnutls_assert(); |
61 | 0 | return GNUTLS_E_HASH_FAILED; |
62 | 0 | } |
63 | | |
64 | 49.4k | dig->hash = cc->hash; |
65 | 49.4k | dig->output = cc->output; |
66 | 49.4k | dig->deinit = cc->deinit; |
67 | 49.4k | dig->copy = cc->copy; |
68 | | |
69 | 49.4k | return 0; |
70 | 49.4k | } |
71 | | |
72 | 3.21M | result = _gnutls_digest_backend()->init( |
73 | 3.21M | (gnutls_digest_algorithm_t)e->id, &dig->handle); |
74 | 3.21M | if (result < 0) { |
75 | 0 | gnutls_assert(); |
76 | 0 | return result; |
77 | 0 | } |
78 | | |
79 | 3.21M | dig->hash = _gnutls_digest_backend()->hash; |
80 | 3.21M | dig->output = _gnutls_digest_backend()->output; |
81 | 3.21M | dig->deinit = _gnutls_digest_backend()->deinit; |
82 | 3.21M | dig->copy = _gnutls_digest_backend()->copy; |
83 | | |
84 | 3.21M | return 0; |
85 | 3.21M | } |
86 | | |
87 | | /* Returns true(non-zero) or false(0) if the |
88 | | * provided hash exists |
89 | | */ |
90 | | int _gnutls_digest_exists(gnutls_digest_algorithm_t algo) |
91 | 3.11k | { |
92 | 3.11k | const gnutls_crypto_digest_st *cc = NULL; |
93 | | |
94 | 3.11k | if (!is_mac_algo_allowed(DIG_TO_MAC(algo))) |
95 | 0 | return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM); |
96 | | |
97 | 3.11k | cc = _gnutls_get_crypto_digest(algo); |
98 | 3.11k | if ( |
99 | | #if defined(ENABLE_PKCS11) && defined(ENABLE_FIPS140) |
100 | | /* Prioritize crypto from pkcs11 provider */ |
101 | | !_p11_provider_is_initialized() && |
102 | | #endif |
103 | 3.11k | cc != NULL) |
104 | 1.35k | return 1; |
105 | | |
106 | 1.75k | return _gnutls_digest_backend()->exists(algo); |
107 | 3.11k | } |
108 | | |
109 | | int _gnutls_hash_copy(const digest_hd_st *handle, digest_hd_st *dst) |
110 | 0 | { |
111 | 0 | if (handle->copy == NULL) |
112 | 0 | return gnutls_assert_val(GNUTLS_E_HASH_FAILED); |
113 | | |
114 | 0 | *dst = *handle; /* copy data */ |
115 | 0 | dst->handle = handle->copy(handle->handle); |
116 | |
|
117 | 0 | if (dst->handle == NULL) |
118 | 0 | return GNUTLS_E_HASH_FAILED; |
119 | | |
120 | 0 | return 0; |
121 | 0 | } |
122 | | |
123 | | void _gnutls_hash_deinit(digest_hd_st *handle, void *digest) |
124 | 3.26M | { |
125 | 3.26M | if (handle->handle == NULL) { |
126 | 0 | return; |
127 | 0 | } |
128 | | |
129 | 3.26M | if (digest != NULL) |
130 | 3.26M | _gnutls_hash_output(handle, digest); |
131 | | |
132 | 3.26M | handle->deinit(handle->handle); |
133 | 3.26M | handle->handle = NULL; |
134 | 3.26M | } |
135 | | |
136 | | int _gnutls_hash_fast(gnutls_digest_algorithm_t algorithm, const void *text, |
137 | | size_t textlen, void *digest) |
138 | 123M | { |
139 | 123M | int ret; |
140 | 123M | const gnutls_crypto_digest_st *cc = NULL; |
141 | | |
142 | 123M | FAIL_IF_LIB_ERROR; |
143 | | |
144 | | /* check if a digest has been registered |
145 | | */ |
146 | 123M | cc = _gnutls_get_crypto_digest(algorithm); |
147 | 123M | if ( |
148 | | #if defined(ENABLE_PKCS11) && defined(ENABLE_FIPS140) |
149 | | /* Prioritize crypto from pkcs11 provider */ |
150 | | !_p11_provider_is_initialized() && |
151 | | #endif |
152 | 123M | cc != NULL) { |
153 | 120M | if (cc->fast(algorithm, text, textlen, digest) < 0) { |
154 | 0 | gnutls_assert(); |
155 | 0 | return GNUTLS_E_HASH_FAILED; |
156 | 0 | } |
157 | | |
158 | 120M | return 0; |
159 | 120M | } |
160 | | |
161 | 2.78M | ret = _gnutls_digest_backend()->fast(algorithm, text, textlen, digest); |
162 | 2.78M | if (ret < 0) { |
163 | 0 | gnutls_assert(); |
164 | 0 | return ret; |
165 | 0 | } |
166 | | |
167 | 2.78M | return 0; |
168 | 2.78M | } |
169 | | |
170 | | int _gnutls_hash_squeeze(digest_hd_st *handle, void *output, size_t length) |
171 | 0 | { |
172 | 0 | if (handle->output == NULL) |
173 | 0 | return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); |
174 | | |
175 | 0 | if (!(handle->e->flags & GNUTLS_MAC_FLAG_XOF)) |
176 | 0 | return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); |
177 | | |
178 | 0 | handle->output(handle->handle, output, length); |
179 | 0 | return 0; |
180 | 0 | } |
181 | | |
182 | | /* HMAC interface */ |
183 | | |
184 | | int _gnutls_mac_fast(gnutls_mac_algorithm_t algorithm, const void *key, |
185 | | int keylen, const void *text, size_t textlen, void *digest) |
186 | 114k | { |
187 | 114k | int ret; |
188 | 114k | const gnutls_crypto_mac_st *cc = NULL; |
189 | | |
190 | 114k | FAIL_IF_LIB_ERROR; |
191 | | |
192 | | /* check if a digest has been registered |
193 | | */ |
194 | 114k | cc = _gnutls_get_crypto_mac(algorithm); |
195 | 114k | if ( |
196 | | #if defined(ENABLE_PKCS11) && defined(ENABLE_FIPS140) |
197 | | /* Prioritize crypto from pkcs11 provider */ |
198 | | !_p11_provider_is_initialized() && |
199 | | #endif |
200 | 114k | cc != NULL) { |
201 | 114k | if (cc->fast(algorithm, NULL, 0, key, keylen, text, textlen, |
202 | 114k | digest) < 0) { |
203 | 0 | gnutls_assert(); |
204 | 0 | return GNUTLS_E_HASH_FAILED; |
205 | 0 | } |
206 | | |
207 | 114k | return 0; |
208 | 114k | } |
209 | | |
210 | 0 | ret = _gnutls_mac_backend()->fast(algorithm, NULL, 0, key, keylen, text, |
211 | 0 | textlen, digest); |
212 | 0 | if (ret < 0) { |
213 | 0 | gnutls_assert(); |
214 | 0 | return ret; |
215 | 0 | } |
216 | | |
217 | 0 | return 0; |
218 | 0 | } |
219 | | |
220 | | /* Returns true(non-zero) or false(0) if the |
221 | | * provided hash exists |
222 | | */ |
223 | | int _gnutls_mac_exists(gnutls_mac_algorithm_t algo) |
224 | 1.81k | { |
225 | 1.81k | const gnutls_crypto_mac_st *cc = NULL; |
226 | | |
227 | | /* exceptionally it exists, as it is not a real MAC */ |
228 | 1.81k | if (algo == GNUTLS_MAC_AEAD) |
229 | 0 | return 1; |
230 | | |
231 | 1.81k | if (!is_mac_algo_allowed(algo)) |
232 | 0 | return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM); |
233 | | |
234 | 1.81k | cc = _gnutls_get_crypto_mac(algo); |
235 | 1.81k | if ( |
236 | | #if defined(ENABLE_PKCS11) && defined(ENABLE_FIPS140) |
237 | | /* Prioritize crypto from pkcs11 provider */ |
238 | | !_p11_provider_is_initialized() && |
239 | | #endif |
240 | 1.81k | cc != NULL) |
241 | 140 | return 1; |
242 | | |
243 | 1.67k | return _gnutls_mac_backend()->exists(algo); |
244 | 1.81k | } |
245 | | |
246 | | int _gnutls_mac_init(mac_hd_st *mac, const mac_entry_st *e, const void *key, |
247 | | int keylen) |
248 | 4.65k | { |
249 | 4.65k | int result; |
250 | 4.65k | const gnutls_crypto_mac_st *cc = NULL; |
251 | | |
252 | 4.65k | FAIL_IF_LIB_ERROR; |
253 | | |
254 | 4.65k | if (unlikely(e == NULL || e->id == GNUTLS_MAC_NULL)) |
255 | 0 | return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); |
256 | | |
257 | 4.65k | mac->e = e; |
258 | 4.65k | mac->mac_len = _gnutls_mac_get_algo_len(e); |
259 | | |
260 | | /* check if a digest has been registered |
261 | | */ |
262 | 4.65k | cc = _gnutls_get_crypto_mac(e->id); |
263 | 4.65k | if ( |
264 | | #if defined(ENABLE_PKCS11) && defined(ENABLE_FIPS140) |
265 | | /* Prioritize crypto from pkcs11 provider */ |
266 | | !_p11_provider_is_initialized() && |
267 | | #endif |
268 | 4.65k | cc != NULL && cc->init != NULL) { |
269 | | |
270 | 3.96k | if (cc->init(e->id, &mac->handle) < 0) { |
271 | 0 | gnutls_assert(); |
272 | 0 | return GNUTLS_E_HASH_FAILED; |
273 | 0 | } |
274 | | |
275 | 3.96k | if (cc->setkey(mac->handle, key, keylen) < 0) { |
276 | 0 | gnutls_assert(); |
277 | 0 | cc->deinit(mac->handle); |
278 | 0 | return GNUTLS_E_HASH_FAILED; |
279 | 0 | } |
280 | | |
281 | 3.96k | mac->hash = cc->hash; |
282 | 3.96k | mac->setnonce = cc->setnonce; |
283 | 3.96k | mac->output = cc->output; |
284 | 3.96k | mac->deinit = cc->deinit; |
285 | 3.96k | mac->copy = cc->copy; |
286 | 3.96k | mac->setkey = cc->setkey; |
287 | | |
288 | 3.96k | return 0; |
289 | 3.96k | } |
290 | | |
291 | 697 | result = _gnutls_mac_backend()->init(e->id, &mac->handle); |
292 | 697 | if (result < 0) { |
293 | 0 | gnutls_assert(); |
294 | 0 | return result; |
295 | 0 | } |
296 | | |
297 | 697 | mac->hash = _gnutls_mac_backend()->hash; |
298 | 697 | mac->setnonce = _gnutls_mac_backend()->setnonce; |
299 | 697 | mac->output = _gnutls_mac_backend()->output; |
300 | 697 | mac->deinit = _gnutls_mac_backend()->deinit; |
301 | 697 | mac->copy = _gnutls_mac_backend()->copy; |
302 | 697 | mac->setkey = _gnutls_mac_backend()->setkey; |
303 | | |
304 | 697 | if (_gnutls_mac_backend()->setkey(mac->handle, key, keylen) < 0) { |
305 | 0 | gnutls_assert(); |
306 | 0 | mac->deinit(mac->handle); |
307 | 0 | return GNUTLS_E_HASH_FAILED; |
308 | 0 | } |
309 | | |
310 | 697 | return 0; |
311 | 697 | } |
312 | | |
313 | | int _gnutls_mac_copy(const mac_hd_st *handle, mac_hd_st *dst) |
314 | 0 | { |
315 | 0 | if (handle->copy == NULL) |
316 | 0 | return gnutls_assert_val(GNUTLS_E_HASH_FAILED); |
317 | | |
318 | 0 | *dst = *handle; /* copy data */ |
319 | 0 | dst->handle = handle->copy(handle->handle); |
320 | |
|
321 | 0 | if (dst->handle == NULL) |
322 | 0 | return GNUTLS_E_HASH_FAILED; |
323 | | |
324 | 0 | return 0; |
325 | 0 | } |
326 | | |
327 | | void _gnutls_mac_deinit(mac_hd_st *handle, void *digest) |
328 | 4.65k | { |
329 | 4.65k | if (handle->handle == NULL) { |
330 | 0 | return; |
331 | 0 | } |
332 | | |
333 | 4.65k | if (digest) |
334 | 1.05k | _gnutls_mac_output(handle, digest); |
335 | | |
336 | 4.65k | handle->deinit(handle->handle); |
337 | 4.65k | handle->handle = NULL; |
338 | 4.65k | } |
339 | | |
340 | | #ifdef ENABLE_SSL3 |
341 | | inline static int get_padsize(gnutls_mac_algorithm_t algorithm) |
342 | | { |
343 | | switch (algorithm) { |
344 | | case GNUTLS_MAC_MD5: |
345 | | return 48; |
346 | | case GNUTLS_MAC_SHA1: |
347 | | return 40; |
348 | | default: |
349 | | return 0; |
350 | | } |
351 | | } |
352 | | |
353 | | /* Special functions for SSL3 MAC |
354 | | */ |
355 | | |
356 | | int _gnutls_mac_init_ssl3(digest_hd_st *ret, const mac_entry_st *e, void *key, |
357 | | int keylen) |
358 | | { |
359 | | uint8_t ipad[48]; |
360 | | int padsize, result; |
361 | | |
362 | | FAIL_IF_LIB_ERROR; |
363 | | |
364 | | padsize = get_padsize(e->id); |
365 | | if (padsize == 0) { |
366 | | gnutls_assert(); |
367 | | return GNUTLS_E_HASH_FAILED; |
368 | | } |
369 | | |
370 | | memset(ipad, 0x36, padsize); |
371 | | |
372 | | result = _gnutls_hash_init(ret, e); |
373 | | if (result < 0) { |
374 | | gnutls_assert(); |
375 | | return result; |
376 | | } |
377 | | |
378 | | ret->key = key; |
379 | | ret->keysize = keylen; |
380 | | |
381 | | if (keylen > 0) |
382 | | _gnutls_hash(ret, key, keylen); |
383 | | _gnutls_hash(ret, ipad, padsize); |
384 | | |
385 | | return 0; |
386 | | } |
387 | | |
388 | | int _gnutls_mac_output_ssl3(digest_hd_st *handle, void *digest) |
389 | | { |
390 | | uint8_t ret[MAX_HASH_SIZE]; |
391 | | digest_hd_st td; |
392 | | uint8_t opad[48]; |
393 | | int padsize; |
394 | | int block, rc; |
395 | | |
396 | | padsize = get_padsize(handle->e->id); |
397 | | if (padsize == 0) { |
398 | | gnutls_assert(); |
399 | | return GNUTLS_E_INTERNAL_ERROR; |
400 | | } |
401 | | |
402 | | memset(opad, 0x5C, padsize); |
403 | | |
404 | | rc = _gnutls_hash_init(&td, handle->e); |
405 | | if (rc < 0) { |
406 | | gnutls_assert(); |
407 | | return rc; |
408 | | } |
409 | | |
410 | | if (handle->keysize > 0) |
411 | | _gnutls_hash(&td, handle->key, handle->keysize); |
412 | | |
413 | | _gnutls_hash(&td, opad, padsize); |
414 | | block = _gnutls_mac_get_algo_len(handle->e); |
415 | | _gnutls_hash_output(handle, ret); /* get the previous hash */ |
416 | | _gnutls_hash(&td, ret, block); |
417 | | |
418 | | _gnutls_hash_deinit(&td, digest); |
419 | | |
420 | | /* reset handle */ |
421 | | memset(opad, 0x36, padsize); |
422 | | |
423 | | if (handle->keysize > 0) |
424 | | _gnutls_hash(handle, handle->key, handle->keysize); |
425 | | _gnutls_hash(handle, opad, padsize); |
426 | | |
427 | | return 0; |
428 | | } |
429 | | |
430 | | int _gnutls_mac_deinit_ssl3(digest_hd_st *handle, void *digest) |
431 | | { |
432 | | int ret = 0; |
433 | | |
434 | | if (digest != NULL) |
435 | | ret = _gnutls_mac_output_ssl3(handle, digest); |
436 | | _gnutls_hash_deinit(handle, NULL); |
437 | | |
438 | | return ret; |
439 | | } |
440 | | |
441 | | int _gnutls_mac_deinit_ssl3_handshake(digest_hd_st *handle, void *digest, |
442 | | uint8_t *key, uint32_t key_size) |
443 | | { |
444 | | uint8_t ret[MAX_HASH_SIZE]; |
445 | | digest_hd_st td; |
446 | | uint8_t opad[48]; |
447 | | uint8_t ipad[48]; |
448 | | int padsize; |
449 | | int block, rc; |
450 | | |
451 | | padsize = get_padsize(handle->e->id); |
452 | | if (padsize == 0) { |
453 | | gnutls_assert(); |
454 | | rc = GNUTLS_E_INTERNAL_ERROR; |
455 | | goto cleanup; |
456 | | } |
457 | | |
458 | | memset(opad, 0x5C, padsize); |
459 | | memset(ipad, 0x36, padsize); |
460 | | |
461 | | rc = _gnutls_hash_init(&td, handle->e); |
462 | | if (rc < 0) { |
463 | | gnutls_assert(); |
464 | | goto cleanup; |
465 | | } |
466 | | |
467 | | if (key_size > 0) |
468 | | _gnutls_hash(&td, key, key_size); |
469 | | |
470 | | _gnutls_hash(&td, opad, padsize); |
471 | | block = _gnutls_mac_get_algo_len(handle->e); |
472 | | |
473 | | if (key_size > 0) |
474 | | _gnutls_hash(handle, key, key_size); |
475 | | _gnutls_hash(handle, ipad, padsize); |
476 | | _gnutls_hash_deinit(handle, ret); /* get the previous hash */ |
477 | | |
478 | | _gnutls_hash(&td, ret, block); |
479 | | |
480 | | _gnutls_hash_deinit(&td, digest); |
481 | | |
482 | | return 0; |
483 | | |
484 | | cleanup: |
485 | | _gnutls_hash_deinit(handle, NULL); |
486 | | return rc; |
487 | | } |
488 | | |
489 | | static int ssl3_sha(int i, uint8_t *secret, int secret_len, uint8_t *rnd, |
490 | | int rnd_len, void *digest) |
491 | | { |
492 | | int j, ret; |
493 | | uint8_t text1[26]; |
494 | | |
495 | | digest_hd_st td; |
496 | | |
497 | | for (j = 0; j < i + 1; j++) { |
498 | | text1[j] = 65 + i; /* A==65 */ |
499 | | } |
500 | | |
501 | | ret = _gnutls_hash_init(&td, mac_to_entry(GNUTLS_MAC_SHA1)); |
502 | | if (ret < 0) { |
503 | | gnutls_assert(); |
504 | | return ret; |
505 | | } |
506 | | |
507 | | _gnutls_hash(&td, text1, i + 1); |
508 | | _gnutls_hash(&td, secret, secret_len); |
509 | | _gnutls_hash(&td, rnd, rnd_len); |
510 | | |
511 | | _gnutls_hash_deinit(&td, digest); |
512 | | return 0; |
513 | | } |
514 | | |
515 | | #define SHA1_DIGEST_OUTPUT 20 |
516 | | #define MD5_DIGEST_OUTPUT 16 |
517 | | |
518 | | static int ssl3_md5(int i, uint8_t *secret, int secret_len, uint8_t *rnd, |
519 | | int rnd_len, void *digest) |
520 | | { |
521 | | uint8_t tmp[MAX_HASH_SIZE]; |
522 | | digest_hd_st td; |
523 | | int ret; |
524 | | |
525 | | ret = _gnutls_hash_init(&td, mac_to_entry(GNUTLS_MAC_MD5)); |
526 | | if (ret < 0) { |
527 | | gnutls_assert(); |
528 | | return ret; |
529 | | } |
530 | | |
531 | | _gnutls_hash(&td, secret, secret_len); |
532 | | |
533 | | ret = ssl3_sha(i, secret, secret_len, rnd, rnd_len, tmp); |
534 | | if (ret < 0) { |
535 | | gnutls_assert(); |
536 | | _gnutls_hash_deinit(&td, digest); |
537 | | return ret; |
538 | | } |
539 | | |
540 | | _gnutls_hash(&td, tmp, SHA1_DIGEST_OUTPUT); |
541 | | |
542 | | _gnutls_hash_deinit(&td, digest); |
543 | | return 0; |
544 | | } |
545 | | |
546 | | int _gnutls_ssl3_generate_random(void *secret, int secret_len, void *rnd, |
547 | | int rnd_len, int ret_bytes, uint8_t *ret) |
548 | | { |
549 | | int i = 0, copy, output_bytes; |
550 | | uint8_t digest[MAX_HASH_SIZE]; |
551 | | int block = MD5_DIGEST_OUTPUT; |
552 | | int result, times; |
553 | | |
554 | | output_bytes = 0; |
555 | | do { |
556 | | output_bytes += block; |
557 | | } while (output_bytes < ret_bytes); |
558 | | |
559 | | times = output_bytes / block; |
560 | | |
561 | | for (i = 0; i < times; i++) { |
562 | | result = ssl3_md5(i, secret, secret_len, rnd, rnd_len, digest); |
563 | | if (result < 0) { |
564 | | gnutls_assert(); |
565 | | return result; |
566 | | } |
567 | | |
568 | | if ((1 + i) * block < ret_bytes) { |
569 | | copy = block; |
570 | | } else { |
571 | | copy = ret_bytes - (i)*block; |
572 | | } |
573 | | |
574 | | memcpy(&ret[i * block], digest, copy); |
575 | | } |
576 | | |
577 | | return 0; |
578 | | } |
579 | | |
580 | | #endif |