Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
dsa.c
00001 /** 00002 * @file dsa.c 00003 * @brief DSA (Digital Signature Algorithm) 00004 * 00005 * @section License 00006 * 00007 * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. 00008 * 00009 * This file is part of CycloneCrypto Open. 00010 * 00011 * This program is free software; you can redistribute it and/or 00012 * modify it under the terms of the GNU General Public License 00013 * as published by the Free Software Foundation; either version 2 00014 * of the License, or (at your option) any later version. 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU General Public License for more details. 00020 * 00021 * You should have received a copy of the GNU General Public License 00022 * along with this program; if not, write to the Free Software Foundation, 00023 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00024 * 00025 * @section Description 00026 * 00027 * The Digital Signature Algorithm (DSA) is a an algorithm developed by the 00028 * NSA to generate a digital signature for the authentication of electronic 00029 * documents. Refer to FIPS 186-3 for more details 00030 * 00031 * @author Oryx Embedded SARL (www.oryx-embedded.com) 00032 * @version 1.7.6 00033 **/ 00034 00035 //Switch to the appropriate trace level 00036 #define TRACE_LEVEL CRYPTO_TRACE_LEVEL 00037 00038 //Dependencies 00039 #include <stdlib.h> 00040 #include "crypto.h" 00041 #include "dsa.h" 00042 #include "mpi.h" 00043 #include "asn1.h" 00044 #include "debug.h" 00045 00046 //Check crypto library configuration 00047 #if (DSA_SUPPORT == ENABLED) 00048 00049 //DSA OID (1.2.840.10040.4.1) 00050 const uint8_t DSA_OID[7] = {0x2A, 0x86, 0x48, 0xCE, 0x38, 0x04, 0x01}; 00051 //DSA with SHA-1 OID (1.2.840.10040.4.3) 00052 const uint8_t DSA_WITH_SHA1_OID[7] = {0x2A, 0x86, 0x48, 0xCE, 0x38, 0x04, 0x03}; 00053 //DSA with SHA-224 OID (2.16.840.1.101.3.4.3.1) 00054 const uint8_t DSA_WITH_SHA224_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x01}; 00055 //DSA with SHA-256 OID (2.16.840.1.101.3.4.3.2) 00056 const uint8_t DSA_WITH_SHA256_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x02}; 00057 //DSA with SHA-384 OID (2.16.840.1.101.3.4.3.3) 00058 const uint8_t DSA_WITH_SHA384_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x03}; 00059 //DSA with SHA-512 OID (2.16.840.1.101.3.4.3.4) 00060 const uint8_t DSA_WITH_SHA512_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x04}; 00061 //DSA with SHA-3-224 OID (2.16.840.1.101.3.4.3.5) 00062 const uint8_t DSA_WITH_SHA3_224_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x05}; 00063 //DSA with SHA-3-256 OID (2.16.840.1.101.3.4.3.6) 00064 const uint8_t DSA_WITH_SHA3_256_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x06}; 00065 //DSA with SHA-3-384 OID (2.16.840.1.101.3.4.3.7) 00066 const uint8_t DSA_WITH_SHA3_384_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x07}; 00067 //DSA with SHA-3-512 OID (2.16.840.1.101.3.4.3.8) 00068 const uint8_t DSA_WITH_SHA3_512_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x08}; 00069 00070 00071 /** 00072 * @brief Initialize a DSA public key 00073 * @param[in] key Pointer to the DSA public key to initialize 00074 **/ 00075 00076 void dsaInitPublicKey(DsaPublicKey *key) 00077 { 00078 //Initialize multiple precision integers 00079 mpiInit(&key->p); 00080 mpiInit(&key->q); 00081 mpiInit(&key->g); 00082 mpiInit(&key->y); 00083 } 00084 00085 00086 /** 00087 * @brief Release a DSA public key 00088 * @param[in] key Pointer to the DSA public key to free 00089 **/ 00090 00091 void dsaFreePublicKey(DsaPublicKey *key) 00092 { 00093 //Free multiple precision integers 00094 mpiFree(&key->p); 00095 mpiFree(&key->q); 00096 mpiFree(&key->g); 00097 mpiFree(&key->y); 00098 } 00099 00100 00101 /** 00102 * @brief Initialize a DSA private key 00103 * @param[in] key Pointer to the DSA private key to initialize 00104 **/ 00105 00106 void dsaInitPrivateKey(DsaPrivateKey *key) 00107 { 00108 //Initialize multiple precision integers 00109 mpiInit(&key->p); 00110 mpiInit(&key->q); 00111 mpiInit(&key->g); 00112 mpiInit(&key->x); 00113 } 00114 00115 00116 /** 00117 * @brief Release a DSA private key 00118 * @param[in] key Pointer to the DSA public key to free 00119 **/ 00120 00121 void dsaFreePrivateKey(DsaPrivateKey *key) 00122 { 00123 //Free multiple precision integers 00124 mpiFree(&key->p); 00125 mpiFree(&key->q); 00126 mpiFree(&key->g); 00127 mpiFree(&key->x); 00128 } 00129 00130 00131 /** 00132 * @brief Initialize a DSA signature 00133 * @param[in] signature Pointer to the DSA signature to initialize 00134 **/ 00135 00136 void dsaInitSignature(DsaSignature *signature) 00137 { 00138 //Initialize multiple precision integers 00139 mpiInit(&signature->r); 00140 mpiInit(&signature->s); 00141 } 00142 00143 00144 /** 00145 * @brief Release a DSA signature 00146 * @param[in] signature Pointer to the DSA signature to free 00147 **/ 00148 00149 void dsaFreeSignature(DsaSignature *signature) 00150 { 00151 //Release multiple precision integers 00152 mpiFree(&signature->r); 00153 mpiFree(&signature->s); 00154 } 00155 00156 00157 /** 00158 * @brief Encode DSA signature using ASN.1 00159 * @param[in] signature (R, S) integer pair 00160 * @param[out] data Pointer to the buffer where to store the resulting ASN.1 structure 00161 * @param[out] length Length of the ASN.1 structure 00162 * @return Error code 00163 **/ 00164 00165 error_t dsaWriteSignature(const DsaSignature *signature, uint8_t *data, size_t *length) 00166 { 00167 error_t error; 00168 size_t k; 00169 size_t n; 00170 size_t rLen; 00171 size_t sLen; 00172 Asn1Tag tag; 00173 00174 //Debug message 00175 TRACE_DEBUG("Writing DSA signature...\r\n"); 00176 00177 //Dump (R, S) integer pair 00178 TRACE_DEBUG(" r:\r\n"); 00179 TRACE_DEBUG_MPI(" ", &signature->r); 00180 TRACE_DEBUG(" s:\r\n"); 00181 TRACE_DEBUG_MPI(" ", &signature->s); 00182 00183 //Calculate the length of R 00184 rLen = mpiGetByteLength(&signature->r); 00185 //Calculate the length of S 00186 sLen = mpiGetByteLength(&signature->s); 00187 00188 //Make sure the (R, S) integer pair is valid 00189 if(rLen == 0 || sLen == 0) 00190 return ERROR_INVALID_LENGTH; 00191 00192 //R and S are always encoded in the smallest possible number of octets 00193 if(mpiGetBitValue(&signature->r, (rLen * 8) - 1)) 00194 rLen++; 00195 if(mpiGetBitValue(&signature->s, (sLen * 8) - 1)) 00196 sLen++; 00197 00198 //The first pass computes the length of the ASN.1 sequence 00199 n = 0; 00200 00201 //The parameter R is encapsulated within an ASN.1 structure 00202 tag.constructed = FALSE; 00203 tag.objClass = ASN1_CLASS_UNIVERSAL; 00204 tag.objType = ASN1_TYPE_INTEGER; 00205 tag.length = rLen; 00206 tag.value = NULL; 00207 00208 //Compute the length of the corresponding ASN.1 structure 00209 error = asn1WriteTag(&tag, FALSE, NULL, NULL); 00210 //Any error to report? 00211 if(error) 00212 return error; 00213 00214 //Update the length of the ASN.1 sequence 00215 n += tag.totalLength; 00216 00217 //The parameter S is encapsulated within an ASN.1 structure 00218 tag.constructed = FALSE; 00219 tag.objClass = ASN1_CLASS_UNIVERSAL; 00220 tag.objType = ASN1_TYPE_INTEGER; 00221 tag.length = sLen; 00222 tag.value = NULL; 00223 00224 //Compute the length of the corresponding ASN.1 structure 00225 error = asn1WriteTag(&tag, FALSE, NULL, NULL); 00226 //Any error to report? 00227 if(error) 00228 return error; 00229 00230 //Update the length of the ASN.1 sequence 00231 n += tag.totalLength; 00232 00233 //The second pass encodes the ASN.1 structure 00234 k = 0; 00235 00236 //The (R, S) integer pair is encapsulated within a sequence 00237 tag.constructed = TRUE; 00238 tag.objClass = ASN1_CLASS_UNIVERSAL; 00239 tag.objType = ASN1_TYPE_SEQUENCE; 00240 tag.length = n; 00241 tag.value = NULL; 00242 00243 //Write the corresponding ASN.1 tag 00244 error = asn1WriteTag(&tag, FALSE, data + k, &n); 00245 //Any error to report? 00246 if(error) 00247 return error; 00248 00249 //Advance write pointer 00250 k += n; 00251 00252 //Encode the parameter R using ASN.1 00253 tag.constructed = FALSE; 00254 tag.objClass = ASN1_CLASS_UNIVERSAL; 00255 tag.objType = ASN1_TYPE_INTEGER; 00256 tag.length = rLen; 00257 tag.value = NULL; 00258 00259 //Write the corresponding ASN.1 tag 00260 error = asn1WriteTag(&tag, FALSE, data + k, &n); 00261 //Any error to report? 00262 if(error) 00263 return error; 00264 00265 //Advance write pointer 00266 k += n; 00267 00268 //Convert R to an octet string 00269 error = mpiWriteRaw(&signature->r, data + k, rLen); 00270 //Any error to report? 00271 if(error) 00272 return error; 00273 00274 //Advance write pointer 00275 k += rLen; 00276 00277 //Encode the parameter S using ASN.1 00278 tag.constructed = FALSE; 00279 tag.objClass = ASN1_CLASS_UNIVERSAL; 00280 tag.objType = ASN1_TYPE_INTEGER; 00281 tag.length = sLen; 00282 tag.value = NULL; 00283 00284 //Write the corresponding ASN.1 tag 00285 error = asn1WriteTag(&tag, FALSE, data + k, &n); 00286 //Any error to report? 00287 if(error) 00288 return error; 00289 00290 //Advance write pointer 00291 k += n; 00292 00293 //Convert S to an octet string 00294 error = mpiWriteRaw(&signature->s, data + k, sLen); 00295 //Any error to report? 00296 if(error) 00297 return error; 00298 00299 //Advance write pointer 00300 k += sLen; 00301 00302 //Dump DSA signature 00303 TRACE_DEBUG(" signature:\r\n"); 00304 TRACE_DEBUG_ARRAY(" ", data, k); 00305 00306 //Total length of the ASN.1 structure 00307 *length = k; 00308 //Successful processing 00309 return NO_ERROR; 00310 } 00311 00312 00313 /** 00314 * @brief Read an ASN.1 encoded DSA signature 00315 * @param[in] data Pointer to the ASN.1 structure to decode 00316 * @param[in] length Length of the ASN.1 structure 00317 * @param[out] signature (R, S) integer pair 00318 * @return Error code 00319 **/ 00320 00321 error_t dsaReadSignature(const uint8_t *data, size_t length, DsaSignature *signature) 00322 { 00323 error_t error; 00324 Asn1Tag tag; 00325 00326 //Debug message 00327 TRACE_DEBUG("Reading DSA signature...\r\n"); 00328 00329 //Dump DSA signature 00330 TRACE_DEBUG(" signature:\r\n"); 00331 TRACE_DEBUG_ARRAY(" ", data, length); 00332 00333 //Start of exception handling block 00334 do 00335 { 00336 //Display ASN.1 structure 00337 error = asn1DumpObject(data, length, 0); 00338 //Any error to report? 00339 if(error) 00340 break; 00341 00342 //Read the contents of the ASN.1 structure 00343 error = asn1ReadTag(data, length, &tag); 00344 //Failed to decode ASN.1 tag? 00345 if(error) 00346 break; 00347 00348 //Enforce encoding, class and type 00349 error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE); 00350 //The tag does not match the criteria? 00351 if(error) 00352 break; 00353 00354 //Point to the first field 00355 data = tag.value; 00356 length = tag.length; 00357 00358 //Read the parameter R 00359 error = asn1ReadTag(data, length, &tag); 00360 //Failed to decode ASN.1 tag? 00361 if(error) 00362 break; 00363 00364 //Enforce encoding, class and type 00365 error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER); 00366 //The tag does not match the criteria? 00367 if(error) 00368 break; 00369 00370 //Convert the octet string to a multiple precision integer 00371 error = mpiReadRaw(&signature->r, tag.value, tag.length); 00372 //Any error to report? 00373 if(error) 00374 break; 00375 00376 //Point to the next field 00377 data += tag.totalLength; 00378 length -= tag.totalLength; 00379 00380 //Read the parameter S 00381 error = asn1ReadTag(data, length, &tag); 00382 //Failed to decode ASN.1 tag? 00383 if(error) 00384 break; 00385 00386 //Enforce encoding, class and type 00387 error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER); 00388 //The tag does not match the criteria? 00389 if(error) 00390 break; 00391 00392 //Convert the octet string to a multiple precision integer 00393 error = mpiReadRaw(&signature->s, tag.value, tag.length); 00394 //Any error to report? 00395 if(error) 00396 break; 00397 00398 //Dump (R, S) integer pair 00399 TRACE_DEBUG(" r:\r\n"); 00400 TRACE_DEBUG_MPI(" ", &signature->r); 00401 TRACE_DEBUG(" s:\r\n"); 00402 TRACE_DEBUG_MPI(" ", &signature->s); 00403 00404 //End of exception handling block 00405 } while(0); 00406 00407 //Clean up side effects if necessary 00408 if(error) 00409 dsaFreeSignature(signature); 00410 00411 //Return status code 00412 return error; 00413 } 00414 00415 00416 /** 00417 * @brief DSA signature generation 00418 * @param[in] prngAlgo PRNG algorithm 00419 * @param[in] prngContext Pointer to the PRNG context 00420 * @param[in] key Signer's DSA private key 00421 * @param[in] digest Digest of the message to be signed 00422 * @param[in] digestLength Length in octets of the digest 00423 * @param[out] signature (R, S) integer pair 00424 * @return Error code 00425 **/ 00426 00427 error_t dsaGenerateSignature(const PrngAlgo *prngAlgo, void *prngContext, 00428 const DsaPrivateKey *key, const uint8_t *digest, size_t digestLength, 00429 DsaSignature *signature) 00430 { 00431 error_t error; 00432 uint_t n; 00433 Mpi k; 00434 Mpi z; 00435 00436 //Check parameters 00437 if(key == NULL || digest == NULL || signature == NULL) 00438 return ERROR_INVALID_PARAMETER; 00439 00440 //Debug message 00441 TRACE_DEBUG("DSA signature generation...\r\n"); 00442 TRACE_DEBUG(" p:\r\n"); 00443 TRACE_DEBUG_MPI(" ", &key->p); 00444 TRACE_DEBUG(" q:\r\n"); 00445 TRACE_DEBUG_MPI(" ", &key->q); 00446 TRACE_DEBUG(" g:\r\n"); 00447 TRACE_DEBUG_MPI(" ", &key->g); 00448 TRACE_DEBUG(" x:\r\n"); 00449 TRACE_DEBUG_MPI(" ", &key->x); 00450 TRACE_DEBUG(" digest:\r\n"); 00451 TRACE_DEBUG_ARRAY(" ", digest, digestLength); 00452 00453 //Initialize multiple precision integers 00454 mpiInit(&k); 00455 mpiInit(&z); 00456 00457 //Let N be the bit length of q 00458 n = mpiGetBitLength(&key->q); 00459 00460 //Generated a pseudorandom number 00461 MPI_CHECK(mpiRand(&k, n, prngAlgo, prngContext)); 00462 00463 //Make sure that 0 < k < q 00464 if(mpiComp(&k, &key->q) >= 0) 00465 mpiShiftRight(&k, 1); 00466 00467 //Debug message 00468 TRACE_DEBUG(" k:\r\n"); 00469 TRACE_DEBUG_MPI(" ", &k); 00470 00471 //Compute N = MIN(N, outlen) 00472 n = MIN(n, digestLength * 8); 00473 00474 //Convert the digest to a multiple precision integer 00475 MPI_CHECK(mpiReadRaw(&z, digest, (n + 7) / 8)); 00476 00477 //Keep the leftmost N bits of the hash value 00478 if(n % 8) 00479 { 00480 MPI_CHECK(mpiShiftRight(&z, 8 - (n % 8))); 00481 } 00482 00483 //Debug message 00484 TRACE_DEBUG(" z:\r\n"); 00485 TRACE_DEBUG_MPI(" ", &z); 00486 00487 //Compute r = (g ^ k mod p) mod q 00488 MPI_CHECK(mpiExpMod(&signature->r, &key->g, &k, &key->p)); 00489 MPI_CHECK(mpiMod(&signature->r, &signature->r, &key->q)); 00490 00491 //Compute k ^ -1 mod q 00492 MPI_CHECK(mpiInvMod(&k, &k, &key->q)); 00493 00494 //Compute s = k ^ -1 * (z + x * r) mod q 00495 MPI_CHECK(mpiMul(&signature->s, &key->x, &signature->r)); 00496 MPI_CHECK(mpiAdd(&signature->s, &signature->s, &z)); 00497 MPI_CHECK(mpiMod(&signature->s, &signature->s, &key->q)); 00498 MPI_CHECK(mpiMulMod(&signature->s, &signature->s, &k, &key->q)); 00499 00500 //Dump DSA signature 00501 TRACE_DEBUG(" r:\r\n"); 00502 TRACE_DEBUG_MPI(" ", &signature->r); 00503 TRACE_DEBUG(" s:\r\n"); 00504 TRACE_DEBUG_MPI(" ", &signature->s); 00505 00506 end: 00507 //Release multiple precision integers 00508 mpiFree(&k); 00509 mpiFree(&z); 00510 00511 //Clean up side effects if necessary 00512 if(error) 00513 { 00514 //Release (R, S) integer pair 00515 mpiFree(&signature->r); 00516 mpiFree(&signature->r); 00517 } 00518 00519 //Return status code 00520 return error; 00521 } 00522 00523 00524 /** 00525 * @brief DSA signature verification 00526 * @param[in] key Signer's DSA public key 00527 * @param[in] digest Digest of the message whose signature is to be verified 00528 * @param[in] digestLength Length in octets of the digest 00529 * @param[in] signature (R, S) integer pair 00530 * @return Error code 00531 **/ 00532 00533 error_t dsaVerifySignature(const DsaPublicKey *key, 00534 const uint8_t *digest, size_t digestLength, const DsaSignature *signature) 00535 { 00536 error_t error; 00537 uint_t n; 00538 Mpi w; 00539 Mpi z; 00540 Mpi u1; 00541 Mpi u2; 00542 Mpi v; 00543 00544 //Check parameters 00545 if(key == NULL || digest == NULL || signature == NULL) 00546 return ERROR_INVALID_PARAMETER; 00547 00548 //Debug message 00549 TRACE_DEBUG("DSA signature verification...\r\n"); 00550 TRACE_DEBUG(" p:\r\n"); 00551 TRACE_DEBUG_MPI(" ", &key->p); 00552 TRACE_DEBUG(" q:\r\n"); 00553 TRACE_DEBUG_MPI(" ", &key->q); 00554 TRACE_DEBUG(" g:\r\n"); 00555 TRACE_DEBUG_MPI(" ", &key->g); 00556 TRACE_DEBUG(" y:\r\n"); 00557 TRACE_DEBUG_MPI(" ", &key->y); 00558 TRACE_DEBUG(" digest:\r\n"); 00559 TRACE_DEBUG_ARRAY(" ", digest, digestLength); 00560 TRACE_DEBUG(" r:\r\n"); 00561 TRACE_DEBUG_MPI(" ", &signature->r); 00562 TRACE_DEBUG(" s:\r\n"); 00563 TRACE_DEBUG_MPI(" ", &signature->s); 00564 00565 //The verifier shall check that 0 < r < q and 0 < s < q. If either 00566 //condition is violated, the signature shall be rejected as invalid 00567 if(mpiCompInt(&signature->r, 0) <= 0 || mpiComp(&signature->r, &key->q) >= 0) 00568 return ERROR_INVALID_SIGNATURE; 00569 if(mpiCompInt(&signature->s, 0) <= 0 || mpiComp(&signature->s, &key->q) >= 0) 00570 return ERROR_INVALID_SIGNATURE; 00571 00572 //Initialize multiple precision integers 00573 mpiInit(&w); 00574 mpiInit(&z); 00575 mpiInit(&u1); 00576 mpiInit(&u2); 00577 mpiInit(&v); 00578 00579 //Let N be the bit length of q 00580 n = mpiGetBitLength(&key->q); 00581 //Compute N = MIN(N, outlen) 00582 n = MIN(n, digestLength * 8); 00583 00584 //Convert the digest to a multiple precision integer 00585 MPI_CHECK(mpiReadRaw(&z, digest, (n + 7) / 8)); 00586 00587 //Keep the leftmost N bits of the hash value 00588 if(n % 8) 00589 { 00590 MPI_CHECK(mpiShiftRight(&z, 8 - (n % 8))); 00591 } 00592 00593 //Compute w = s ^ -1 mod q 00594 MPI_CHECK(mpiInvMod(&w, &signature->s, &key->q)); 00595 //Compute u1 = z * w mod q 00596 MPI_CHECK(mpiMulMod(&u1, &z, &w, &key->q)); 00597 //Compute u2 = r * w mod q 00598 MPI_CHECK(mpiMulMod(&u2, &signature->r, &w, &key->q)); 00599 00600 //Compute v = ((g ^ u1) * (y ^ u2) mod p) mod q 00601 MPI_CHECK(mpiExpMod(&v, &key->g, &u1, &key->p)); 00602 MPI_CHECK(mpiExpMod(&w, &key->y, &u2, &key->p)); 00603 MPI_CHECK(mpiMulMod(&v, &v, &w, &key->p)); 00604 MPI_CHECK(mpiMod(&v, &v, &key->q)); 00605 00606 //Debug message 00607 TRACE_DEBUG(" v:\r\n"); 00608 TRACE_DEBUG_MPI(" ", &v); 00609 00610 //If v = r, then the signature is verified. If v does not equal r, 00611 //then the message or the signature may have been modified 00612 if(!mpiComp(&v, &signature->r)) 00613 error = NO_ERROR; 00614 else 00615 error = ERROR_INVALID_SIGNATURE; 00616 00617 end: 00618 //Release multiple precision integers 00619 mpiFree(&w); 00620 mpiFree(&z); 00621 mpiFree(&u1); 00622 mpiFree(&u2); 00623 mpiFree(&v); 00624 00625 //Return status code 00626 return error; 00627 } 00628 00629 #endif 00630
Generated on Tue Jul 12 2022 17:10:13 by
1.7.2