00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include "xyssl/config.h"
00028
00029 #if defined(XYSSL_RSA_C)
00030
00031 #include "xyssl/rsa.h"
00032
00033 #include <stdlib.h>
00034 #include <string.h>
00035 #include <stdio.h>
00036
00037
00038
00039
00040 void rsa_init( rsa_context *ctx,
00041 int padding,
00042 int hash_id,
00043 int (*f_rng)(void *),
00044 void *p_rng )
00045 {
00046 memset( ctx, 0, sizeof( rsa_context ) );
00047
00048 ctx->padding = padding;
00049 ctx->hash_id = hash_id;
00050
00051 ctx->f_rng = f_rng;
00052 ctx->p_rng = p_rng;
00053 }
00054
00055 #if defined(XYSSL_GENPRIME)
00056
00057
00058
00059
00060 int rsa_gen_key( rsa_context *ctx, int nbits, int exponent )
00061 {
00062 int ret;
00063 mpi P1, Q1, H, G;
00064
00065 if( ctx->f_rng == NULL || nbits < 128 || exponent < 3 )
00066 return( XYSSL_ERR_RSA_BAD_INPUT_DATA );
00067
00068 mpi_init( &P1, &Q1, &H, &G, NULL );
00069
00070
00071
00072
00073
00074 MPI_CHK( mpi_lset( &ctx->E, exponent ) );
00075
00076 do
00077 {
00078 MPI_CHK( mpi_gen_prime( &ctx->P, ( nbits + 1 ) >> 1, 0,
00079 ctx->f_rng, ctx->p_rng ) );
00080
00081 MPI_CHK( mpi_gen_prime( &ctx->Q, ( nbits + 1 ) >> 1, 0,
00082 ctx->f_rng, ctx->p_rng ) );
00083
00084 if( mpi_cmp_mpi( &ctx->P, &ctx->Q ) < 0 )
00085 mpi_swap( &ctx->P, &ctx->Q );
00086
00087 if( mpi_cmp_mpi( &ctx->P, &ctx->Q ) == 0 )
00088 continue;
00089
00090 MPI_CHK( mpi_mul_mpi( &ctx->N, &ctx->P, &ctx->Q ) );
00091 if( mpi_msb( &ctx->N ) != nbits )
00092 continue;
00093
00094 MPI_CHK( mpi_sub_int( &P1, &ctx->P, 1 ) );
00095 MPI_CHK( mpi_sub_int( &Q1, &ctx->Q, 1 ) );
00096 MPI_CHK( mpi_mul_mpi( &H, &P1, &Q1 ) );
00097 MPI_CHK( mpi_gcd( &G, &ctx->E, &H ) );
00098 }
00099 while( mpi_cmp_int( &G, 1 ) != 0 );
00100
00101
00102
00103
00104
00105
00106
00107 MPI_CHK( mpi_inv_mod( &ctx->D , &ctx->E, &H ) );
00108 MPI_CHK( mpi_mod_mpi( &ctx->DP, &ctx->D, &P1 ) );
00109 MPI_CHK( mpi_mod_mpi( &ctx->DQ, &ctx->D, &Q1 ) );
00110 MPI_CHK( mpi_inv_mod( &ctx->QP, &ctx->Q, &ctx->P ) );
00111
00112 ctx->len = ( mpi_msb( &ctx->N ) + 7 ) >> 3;
00113
00114 cleanup:
00115
00116 mpi_free( &G, &H, &Q1, &P1, NULL );
00117
00118 if( ret != 0 )
00119 {
00120 rsa_free( ctx );
00121 return( XYSSL_ERR_RSA_KEY_GEN_FAILED | ret );
00122 }
00123
00124 return( 0 );
00125 }
00126
00127 #endif
00128
00129
00130
00131
00132 int rsa_check_pubkey( rsa_context *ctx )
00133 {
00134 if( ( ctx->N.p[0] & 1 ) == 0 ||
00135 ( ctx->E.p[0] & 1 ) == 0 )
00136 return( XYSSL_ERR_RSA_KEY_CHECK_FAILED );
00137
00138 if( mpi_msb( &ctx->N ) < 128 ||
00139 mpi_msb( &ctx->N ) > 4096 )
00140 return( XYSSL_ERR_RSA_KEY_CHECK_FAILED );
00141
00142 if( mpi_msb( &ctx->E ) < 2 ||
00143 mpi_msb( &ctx->E ) > 64 )
00144 return( XYSSL_ERR_RSA_KEY_CHECK_FAILED );
00145
00146 return( 0 );
00147 }
00148
00149
00150
00151
00152 int rsa_check_privkey( rsa_context *ctx )
00153 {
00154 int ret;
00155 mpi PQ, DE, P1, Q1, H, I, G;
00156
00157 if( ( ret = rsa_check_pubkey( ctx ) ) != 0 )
00158 return( ret );
00159
00160 mpi_init( &PQ, &DE, &P1, &Q1, &H, &I, &G, NULL );
00161
00162 MPI_CHK( mpi_mul_mpi( &PQ, &ctx->P, &ctx->Q ) );
00163 MPI_CHK( mpi_mul_mpi( &DE, &ctx->D, &ctx->E ) );
00164 MPI_CHK( mpi_sub_int( &P1, &ctx->P, 1 ) );
00165 MPI_CHK( mpi_sub_int( &Q1, &ctx->Q, 1 ) );
00166 MPI_CHK( mpi_mul_mpi( &H, &P1, &Q1 ) );
00167 MPI_CHK( mpi_mod_mpi( &I, &DE, &H ) );
00168 MPI_CHK( mpi_gcd( &G, &ctx->E, &H ) );
00169
00170 if( mpi_cmp_mpi( &PQ, &ctx->N ) == 0 &&
00171 mpi_cmp_int( &I, 1 ) == 0 &&
00172 mpi_cmp_int( &G, 1 ) == 0 )
00173 {
00174 mpi_free( &G, &I, &H, &Q1, &P1, &DE, &PQ, NULL );
00175 return( 0 );
00176 }
00177
00178 cleanup:
00179
00180 mpi_free( &G, &I, &H, &Q1, &P1, &DE, &PQ, NULL );
00181 return( XYSSL_ERR_RSA_KEY_CHECK_FAILED | ret );
00182 }
00183
00184
00185
00186
00187 int rsa_public( rsa_context *ctx,
00188 unsigned char *input,
00189 unsigned char *output )
00190 {
00191 int ret, olen;
00192 mpi T;
00193
00194 mpi_init( &T, NULL );
00195
00196 MPI_CHK( mpi_read_binary( &T, input, ctx->len ) );
00197
00198 if( mpi_cmp_mpi( &T, &ctx->N ) >= 0 )
00199 {
00200 mpi_free( &T, NULL );
00201 return( XYSSL_ERR_RSA_BAD_INPUT_DATA );
00202 }
00203
00204 olen = ctx->len;
00205 MPI_CHK( mpi_exp_mod( &T, &T, &ctx->E, &ctx->N, &ctx->RN ) );
00206 MPI_CHK( mpi_write_binary( &T, output, olen ) );
00207
00208 cleanup:
00209
00210 mpi_free( &T, NULL );
00211
00212 if( ret != 0 )
00213 return( XYSSL_ERR_RSA_PUBLIC_FAILED | ret );
00214
00215 return( 0 );
00216 }
00217
00218
00219
00220
00221 int rsa_private( rsa_context *ctx,
00222 unsigned char *input,
00223 unsigned char *output )
00224 {
00225 int ret, olen;
00226 mpi T, T1, T2;
00227
00228 mpi_init( &T, &T1, &T2, NULL );
00229
00230 MPI_CHK( mpi_read_binary( &T, input, ctx->len ) );
00231
00232 if( mpi_cmp_mpi( &T, &ctx->N ) >= 0 )
00233 {
00234 mpi_free( &T, NULL );
00235 return( XYSSL_ERR_RSA_BAD_INPUT_DATA );
00236 }
00237
00238 #if 0
00239 MPI_CHK( mpi_exp_mod( &T, &T, &ctx->D, &ctx->N, &ctx->RN ) );
00240 #else
00241
00242
00243
00244
00245
00246
00247 MPI_CHK( mpi_exp_mod( &T1, &T, &ctx->DP, &ctx->P, &ctx->RP ) );
00248 MPI_CHK( mpi_exp_mod( &T2, &T, &ctx->DQ, &ctx->Q, &ctx->RQ ) );
00249
00250
00251
00252
00253 MPI_CHK( mpi_sub_mpi( &T, &T1, &T2 ) );
00254 MPI_CHK( mpi_mul_mpi( &T1, &T, &ctx->QP ) );
00255 MPI_CHK( mpi_mod_mpi( &T, &T1, &ctx->P ) );
00256
00257
00258
00259
00260 MPI_CHK( mpi_mul_mpi( &T1, &T, &ctx->Q ) );
00261 MPI_CHK( mpi_add_mpi( &T, &T2, &T1 ) );
00262 #endif
00263
00264 olen = ctx->len;
00265 MPI_CHK( mpi_write_binary( &T, output, olen ) );
00266
00267 cleanup:
00268
00269 mpi_free( &T, &T1, &T2, NULL );
00270
00271 if( ret != 0 )
00272 return( XYSSL_ERR_RSA_PRIVATE_FAILED | ret );
00273
00274 return( 0 );
00275 }
00276
00277
00278
00279
00280 int rsa_pkcs1_encrypt( rsa_context *ctx,
00281 int mode, int ilen,
00282 unsigned char *input,
00283 unsigned char *output )
00284 {
00285 int nb_pad, olen;
00286 unsigned char *p = output;
00287
00288 olen = ctx->len;
00289
00290 switch( ctx->padding )
00291 {
00292 case RSA_PKCS_V15:
00293
00294 if( ilen < 0 || olen < ilen + 11 )
00295 return( XYSSL_ERR_RSA_BAD_INPUT_DATA );
00296
00297 nb_pad = olen - 3 - ilen;
00298
00299 *p++ = 0;
00300 *p++ = RSA_CRYPT;
00301
00302 while( nb_pad-- > 0 )
00303 {
00304 do {
00305 *p = (unsigned char) rand();
00306 } while( *p == 0 );
00307 p++;
00308 }
00309 *p++ = 0;
00310 memcpy( p, input, ilen );
00311 break;
00312
00313 default:
00314
00315 return( XYSSL_ERR_RSA_INVALID_PADDING );
00316 }
00317
00318 return( ( mode == RSA_PUBLIC )
00319 ? rsa_public( ctx, output, output )
00320 : rsa_private( ctx, output, output ) );
00321 }
00322
00323
00324
00325
00326 int rsa_pkcs1_decrypt( rsa_context *ctx,
00327 int mode, int *olen,
00328 unsigned char *input,
00329 unsigned char *output )
00330 {
00331 int ret, ilen;
00332 unsigned char *p;
00333 unsigned char buf[512];
00334
00335 ilen = ctx->len;
00336
00337 if( ilen < 16 || ilen > (int) sizeof( buf ) )
00338 return( XYSSL_ERR_RSA_BAD_INPUT_DATA );
00339
00340 ret = ( mode == RSA_PUBLIC )
00341 ? rsa_public( ctx, input, buf )
00342 : rsa_private( ctx, input, buf );
00343
00344 if( ret != 0 )
00345 return( ret );
00346
00347 p = buf;
00348
00349 switch( ctx->padding )
00350 {
00351 case RSA_PKCS_V15:
00352
00353 if( *p++ != 0 || *p++ != RSA_CRYPT )
00354 return( XYSSL_ERR_RSA_INVALID_PADDING );
00355
00356 while( *p != 0 )
00357 {
00358 if( p >= buf + ilen - 1 )
00359 return( XYSSL_ERR_RSA_INVALID_PADDING );
00360 p++;
00361 }
00362 p++;
00363 break;
00364
00365 default:
00366
00367 return( XYSSL_ERR_RSA_INVALID_PADDING );
00368 }
00369
00370 *olen = ilen - (int)(p - buf);
00371 memcpy( output, p, *olen );
00372
00373 return( 0 );
00374 }
00375
00376
00377
00378
00379 int rsa_pkcs1_sign( rsa_context *ctx,
00380 int mode,
00381 int hash_id,
00382 int hashlen,
00383 unsigned char *hash,
00384 unsigned char *sig )
00385 {
00386 int nb_pad, olen;
00387 unsigned char *p = sig;
00388
00389 olen = ctx->len;
00390
00391 switch( ctx->padding )
00392 {
00393 case RSA_PKCS_V15:
00394
00395 switch( hash_id )
00396 {
00397 case RSA_RAW:
00398 nb_pad = olen - 3 - hashlen;
00399 break;
00400
00401 case RSA_MD2:
00402 case RSA_MD4:
00403 case RSA_MD5:
00404 nb_pad = olen - 3 - 34;
00405 break;
00406
00407 case RSA_SHA1:
00408 nb_pad = olen - 3 - 35;
00409 break;
00410
00411 default:
00412 return( XYSSL_ERR_RSA_BAD_INPUT_DATA );
00413 }
00414
00415 if( nb_pad < 8 )
00416 return( XYSSL_ERR_RSA_BAD_INPUT_DATA );
00417
00418 *p++ = 0;
00419 *p++ = RSA_SIGN;
00420 memset( p, 0xFF, nb_pad );
00421 p += nb_pad;
00422 *p++ = 0;
00423 break;
00424
00425 default:
00426
00427 return( XYSSL_ERR_RSA_INVALID_PADDING );
00428 }
00429
00430 switch( hash_id )
00431 {
00432 case RSA_RAW:
00433 memcpy( p, hash, hashlen );
00434 break;
00435
00436 case RSA_MD2:
00437 memcpy( p, ASN1_HASH_MDX, 18 );
00438 memcpy( p + 18, hash, 16 );
00439 p[13] = 2; break;
00440
00441 case RSA_MD4:
00442 memcpy( p, ASN1_HASH_MDX, 18 );
00443 memcpy( p + 18, hash, 16 );
00444 p[13] = 4; break;
00445
00446 case RSA_MD5:
00447 memcpy( p, ASN1_HASH_MDX, 18 );
00448 memcpy( p + 18, hash, 16 );
00449 p[13] = 5; break;
00450
00451 case RSA_SHA1:
00452 memcpy( p, ASN1_HASH_SHA1, 15 );
00453 memcpy( p + 15, hash, 20 );
00454 break;
00455
00456 default:
00457 return( XYSSL_ERR_RSA_BAD_INPUT_DATA );
00458 }
00459
00460 return( ( mode == RSA_PUBLIC )
00461 ? rsa_public( ctx, sig, sig )
00462 : rsa_private( ctx, sig, sig ) );
00463 }
00464
00465
00466
00467
00468 int rsa_pkcs1_verify( rsa_context *ctx,
00469 int mode,
00470 int hash_id,
00471 int hashlen,
00472 unsigned char *hash,
00473 unsigned char *sig )
00474 {
00475 int ret, len, siglen;
00476 unsigned char *p, c;
00477 unsigned char buf[512];
00478
00479 siglen = ctx->len;
00480
00481 if( siglen < 16 || siglen > (int) sizeof( buf ) )
00482 return( XYSSL_ERR_RSA_BAD_INPUT_DATA );
00483
00484 ret = ( mode == RSA_PUBLIC )
00485 ? rsa_public( ctx, sig, buf )
00486 : rsa_private( ctx, sig, buf );
00487
00488 if( ret != 0 )
00489 return( ret );
00490
00491 p = buf;
00492
00493 switch( ctx->padding )
00494 {
00495 case RSA_PKCS_V15:
00496
00497 if( *p++ != 0 || *p++ != RSA_SIGN )
00498 return( XYSSL_ERR_RSA_INVALID_PADDING );
00499
00500 while( *p != 0 )
00501 {
00502 if( p >= buf + siglen - 1 || *p != 0xFF )
00503 return( XYSSL_ERR_RSA_INVALID_PADDING );
00504 p++;
00505 }
00506 p++;
00507 break;
00508
00509 default:
00510
00511 return( XYSSL_ERR_RSA_INVALID_PADDING );
00512 }
00513
00514 len = siglen - (int)( p - buf );
00515
00516 if( len == 34 )
00517 {
00518 c = p[13];
00519 p[13] = 0;
00520
00521 if( memcmp( p, ASN1_HASH_MDX, 18 ) != 0 )
00522 return( XYSSL_ERR_RSA_VERIFY_FAILED );
00523
00524 if( ( c == 2 && hash_id == RSA_MD2 ) ||
00525 ( c == 4 && hash_id == RSA_MD4 ) ||
00526 ( c == 5 && hash_id == RSA_MD5 ) )
00527 {
00528 if( memcmp( p + 18, hash, 16 ) == 0 )
00529 return( 0 );
00530 else
00531 return( XYSSL_ERR_RSA_VERIFY_FAILED );
00532 }
00533 }
00534
00535 if( len == 35 && hash_id == RSA_SHA1 )
00536 {
00537 if( memcmp( p, ASN1_HASH_SHA1, 15 ) == 0 &&
00538 memcmp( p + 15, hash, 20 ) == 0 )
00539 return( 0 );
00540 else
00541 return( XYSSL_ERR_RSA_VERIFY_FAILED );
00542 }
00543
00544 if( len == hashlen && hash_id == RSA_RAW )
00545 {
00546 if( memcmp( p, hash, hashlen ) == 0 )
00547 return( 0 );
00548 else
00549 return( XYSSL_ERR_RSA_VERIFY_FAILED );
00550 }
00551
00552 return( XYSSL_ERR_RSA_INVALID_PADDING );
00553 }
00554
00555
00556
00557
00558 void rsa_free( rsa_context *ctx )
00559 {
00560 mpi_free( &ctx->RQ, &ctx->RP, &ctx->RN,
00561 &ctx->QP, &ctx->DQ, &ctx->DP,
00562 &ctx->Q, &ctx->P, &ctx->D,
00563 &ctx->E, &ctx->N, NULL );
00564 }
00565
00566 #if defined(XYSSL_SELF_TEST)
00567
00568 #include "xyssl/sha1.h"
00569
00570
00571
00572
00573 #define KEY_LEN 128
00574
00575 #define RSA_N "9292758453063D803DD603D5E777D788" \
00576 "8ED1D5BF35786190FA2F23EBC0848AEA" \
00577 "DDA92CA6C3D80B32C4D109BE0F36D6AE" \
00578 "7130B9CED7ACDF54CFC7555AC14EEBAB" \
00579 "93A89813FBF3C4F8066D2D800F7C38A8" \
00580 "1AE31942917403FF4946B0A83D3D3E05" \
00581 "EE57C6F5F5606FB5D4BC6CD34EE0801A" \
00582 "5E94BB77B07507233A0BC7BAC8F90F79"
00583
00584 #define RSA_E "10001"
00585
00586 #define RSA_D "24BF6185468786FDD303083D25E64EFC" \
00587 "66CA472BC44D253102F8B4A9D3BFA750" \
00588 "91386C0077937FE33FA3252D28855837" \
00589 "AE1B484A8A9A45F7EE8C0C634F99E8CD" \
00590 "DF79C5CE07EE72C7F123142198164234" \
00591 "CABB724CF78B8173B9F880FC86322407" \
00592 "AF1FEDFDDE2BEB674CA15F3E81A1521E" \
00593 "071513A1E85B5DFA031F21ECAE91A34D"
00594
00595 #define RSA_P "C36D0EB7FCD285223CFB5AABA5BDA3D8" \
00596 "2C01CAD19EA484A87EA4377637E75500" \
00597 "FCB2005C5C7DD6EC4AC023CDA285D796" \
00598 "C3D9E75E1EFC42488BB4F1D13AC30A57"
00599
00600 #define RSA_Q "C000DF51A7C77AE8D7C7370C1FF55B69" \
00601 "E211C2B9E5DB1ED0BF61D0D9899620F4" \
00602 "910E4168387E3C30AA1E00C339A79508" \
00603 "8452DD96A9A5EA5D9DCA68DA636032AF"
00604
00605 #define RSA_DP "C1ACF567564274FB07A0BBAD5D26E298" \
00606 "3C94D22288ACD763FD8E5600ED4A702D" \
00607 "F84198A5F06C2E72236AE490C93F07F8" \
00608 "3CC559CD27BC2D1CA488811730BB5725"
00609
00610 #define RSA_DQ "4959CBF6F8FEF750AEE6977C155579C7" \
00611 "D8AAEA56749EA28623272E4F7D0592AF" \
00612 "7C1F1313CAC9471B5C523BFE592F517B" \
00613 "407A1BD76C164B93DA2D32A383E58357"
00614
00615 #define RSA_QP "9AE7FBC99546432DF71896FC239EADAE" \
00616 "F38D18D2B2F0E2DD275AA977E2BF4411" \
00617 "F5A3B2A5D33605AEBBCCBA7FEB9F2D2F" \
00618 "A74206CEC169D74BF5A8C50D6F48EA08"
00619
00620 #define PT_LEN 24
00621 #define RSA_PT "\xAA\xBB\xCC\x03\x02\x01\x00\xFF\xFF\xFF\xFF\xFF" \
00622 "\x11\x22\x33\x0A\x0B\x0C\xCC\xDD\xDD\xDD\xDD\xDD"
00623
00624
00625
00626
00627 int rsa_self_test( int verbose )
00628 {
00629 int len;
00630 rsa_context rsa;
00631 unsigned char sha1sum[20];
00632 unsigned char rsa_plaintext[PT_LEN];
00633 unsigned char rsa_decrypted[PT_LEN];
00634 unsigned char rsa_ciphertext[KEY_LEN];
00635
00636 memset( &rsa, 0, sizeof( rsa_context ) );
00637
00638 rsa.len = KEY_LEN;
00639 mpi_read_string( &rsa.N , 16, RSA_N );
00640 mpi_read_string( &rsa.E , 16, RSA_E );
00641 mpi_read_string( &rsa.D , 16, RSA_D );
00642 mpi_read_string( &rsa.P , 16, RSA_P );
00643 mpi_read_string( &rsa.Q , 16, RSA_Q );
00644 mpi_read_string( &rsa.DP, 16, RSA_DP );
00645 mpi_read_string( &rsa.DQ, 16, RSA_DQ );
00646 mpi_read_string( &rsa.QP, 16, RSA_QP );
00647
00648 if( verbose != 0 )
00649 printf( " RSA key validation: " );
00650
00651 if( rsa_check_pubkey( &rsa ) != 0 ||
00652 rsa_check_privkey( &rsa ) != 0 )
00653 {
00654 if( verbose != 0 )
00655 printf( "failed\n" );
00656
00657 return( 1 );
00658 }
00659
00660 if( verbose != 0 )
00661 printf( "passed\n PKCS#1 encryption : " );
00662
00663 memcpy( rsa_plaintext, RSA_PT, PT_LEN );
00664
00665 if( rsa_pkcs1_encrypt( &rsa, RSA_PUBLIC, PT_LEN,
00666 rsa_plaintext, rsa_ciphertext ) != 0 )
00667 {
00668 if( verbose != 0 )
00669 printf( "failed\n" );
00670
00671 return( 1 );
00672 }
00673
00674 if( verbose != 0 )
00675 printf( "passed\n PKCS#1 decryption : " );
00676
00677 if( rsa_pkcs1_decrypt( &rsa, RSA_PRIVATE, &len,
00678 rsa_ciphertext, rsa_decrypted ) != 0 )
00679 {
00680 if( verbose != 0 )
00681 printf( "failed\n" );
00682
00683 return( 1 );
00684 }
00685
00686 if( memcmp( rsa_decrypted, rsa_plaintext, len ) != 0 )
00687 {
00688 if( verbose != 0 )
00689 printf( "failed\n" );
00690
00691 return( 1 );
00692 }
00693
00694 if( verbose != 0 )
00695 printf( "passed\n PKCS#1 data sign : " );
00696
00697 sha1( rsa_plaintext, PT_LEN, sha1sum );
00698
00699 if( rsa_pkcs1_sign( &rsa, RSA_PRIVATE, RSA_SHA1, 20,
00700 sha1sum, rsa_ciphertext ) != 0 )
00701 {
00702 if( verbose != 0 )
00703 printf( "failed\n" );
00704
00705 return( 1 );
00706 }
00707
00708 if( verbose != 0 )
00709 printf( "passed\n PKCS#1 sig. verify: " );
00710
00711 if( rsa_pkcs1_verify( &rsa, RSA_PUBLIC, RSA_SHA1, 20,
00712 sha1sum, rsa_ciphertext ) != 0 )
00713 {
00714 if( verbose != 0 )
00715 printf( "failed\n" );
00716
00717 return( 1 );
00718 }
00719
00720 if( verbose != 0 )
00721 printf( "passed\n\n" );
00722
00723 rsa_free( &rsa );
00724
00725 return( 0 );
00726 }
00727
00728 #endif
00729
00730 #endif