Simulated product dispenser

Dependencies:   HTS221

Fork of mbed-cloud-workshop-connect-HTS221 by Jim Carver

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers arm_uc_mmFetchFirmwareInfo.c Source File

arm_uc_mmFetchFirmwareInfo.c

00001 // ----------------------------------------------------------------------------
00002 // Copyright 2016-2017 ARM Ltd.
00003 //
00004 // SPDX-License-Identifier: Apache-2.0
00005 //
00006 // Licensed under the Apache License, Version 2.0 (the "License");
00007 // you may not use this file except in compliance with the License.
00008 // You may obtain a copy of the License at
00009 //
00010 //     http://www.apache.org/licenses/LICENSE-2.0
00011 //
00012 // Unless required by applicable law or agreed to in writing, software
00013 // distributed under the License is distributed on an "AS IS" BASIS,
00014 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00015 // See the License for the specific language governing permissions and
00016 // limitations under the License.
00017 // ----------------------------------------------------------------------------
00018 
00019 #include "arm_uc_mmCryptoUtils.h"
00020 #include "arm_uc_mmCommon.h"
00021 #include "arm_uc_mmConfig.h"
00022 #include "arm_uc_mmDerManifestAccessors.h"
00023 #include "arm_uc_mmDerManifestParser.h"
00024 #include "arm_uc_mmFSMHelper.h"
00025 #include "update-client-common/arm_uc_scheduler.h"
00026 
00027 #include "update-client-manifest-manager/update-client-manifest-manager.h"
00028 #include "update-client-manifest-manager/update-client-manifest-types.h"
00029 
00030 #include "pal.h"
00031 
00032 #include <stdint.h>
00033 #include <stdio.h>
00034 #include <stddef.h>
00035 #include <string.h>
00036 
00037 #define htobe PAL_HTONL
00038 #ifndef htobe
00039 static inline uint32_t htobe(uint32_t x)
00040 {
00041 #if BYTE_ORDER == LITTLE_ENDIAN
00042     return __builtin_bswap32(x);
00043 #else
00044     return x;
00045 #endif
00046 }
00047 #endif
00048 
00049 #undef ARRAY_SIZE
00050 #define ARRAY_SIZE(ENUM_AUTO)\
00051     (sizeof(ENUM_AUTO)/sizeof((ENUM_AUTO)[0]))
00052 
00053 
00054 #define ARM_UC_MM_MFST_IMAGE_REF_FIELDS \
00055     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_FMT_ENUM)\
00056     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_STRG_ID)\
00057     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_RSRC_REF_HASH)\
00058     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_RSRC_REF_URL)\
00059     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_RSRC_REF_SIZE)\
00060 
00061 static const int32_t imageRefFields [] = {
00062     #define ENUM_AUTO(ENUM_AUTO) ENUM_AUTO,
00063     ARM_UC_MM_MFST_IMAGE_REF_FIELDS
00064     #undef ENUM_AUTO
00065 };
00066 enum imageRefFieldIdxs {
00067     #define ENUM_AUTO(ENUM_AUTO) IRF_ ## ENUM_AUTO ## _IDX,
00068     ARM_UC_MM_MFST_IMAGE_REF_FIELDS
00069     #undef ENUM_AUTO
00070 };
00071 
00072 #if ARM_UC_MANIFEST_MANAGER_TRACE_ENABLE
00073 static const char* ARM_UC_mmFwState2Str(uint32_t state)
00074 {
00075     switch (state) {
00076         #define ENUM_AUTO(name) case name: return #name;
00077         #define ENUM_FIXED(name, val) ENUM_AUTO(name)
00078         ARM_UC_MM_FW_STATE_LIST
00079         #undef ENUM_AUTO
00080         #undef ENUM_FIXED
00081     default:
00082         return "Unknown State";
00083     }
00084 }
00085 #endif
00086 
00087 int ARM_UC_mmGetImageRef(manifest_firmware_info_t* info, arm_uc_buffer_t* mfst_fwref)
00088 {
00089     arm_uc_buffer_t buffers[ARRAY_SIZE(imageRefFields)];
00090     int rc = ARM_UC_mmDERParseTree(arm_uc_mmManifestFirmwareDescription,
00091                           mfst_fwref,
00092                           ARRAY_SIZE(imageRefFields),
00093                           imageRefFields,
00094                           buffers);
00095     if (rc == 0)
00096     {
00097         // Found local key ID and encrypted key
00098         info->cipherMode = ARM_UC_MM_CIPHERMODE_NONE;
00099         // TODO: Handle non-enum format
00100         uint32_t format = ARM_UC_mmDerBuf2Uint(&buffers[IRF_ARM_UC_MM_DER_MFST_FW_FMT_ENUM_IDX]);
00101         memset(&info->format, 0, sizeof(info->format));
00102         info->format.words[RFC_4122_WORDS-1] = htobe(format);
00103 
00104         ARM_UC_buffer_shallow_copy(&info->strgId, &buffers[IRF_ARM_UC_MM_DER_MFST_FW_STRG_ID_IDX]);
00105         ARM_UC_buffer_shallow_copy(&info->hash, &buffers[IRF_ARM_UC_MM_DER_MFST_FW_RSRC_REF_HASH_IDX]);
00106         ARM_UC_buffer_shallow_copy(&info->uri, &buffers[IRF_ARM_UC_MM_DER_MFST_FW_RSRC_REF_URL_IDX]);
00107         info->size = ARM_UC_mmDerBuf2Uint(&buffers[IRF_ARM_UC_MM_DER_MFST_FW_RSRC_REF_SIZE_IDX]);
00108     }
00109     return rc;
00110 }
00111 
00112 #define ARM_UC_MM_MFST_CRYPT_LOCAL_ID_FIELDS \
00113     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_FMT_ENUM)\
00114     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_CRYPT_IV)\
00115     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_CRYPT_ID_LOCAL)\
00116     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_CRYPT_KEY_CIPHERKEY)\
00117     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_STRG_ID)\
00118     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_RSRC_REF_HASH)\
00119     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_RSRC_REF_URL)\
00120     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_RSRC_REF_SIZE)\
00121 
00122 static const int32_t localEncKeyFields [] = {
00123     #define ENUM_AUTO(ENUM_AUTO) ENUM_AUTO,
00124     ARM_UC_MM_MFST_CRYPT_LOCAL_ID_FIELDS
00125     #undef ENUM_AUTO
00126 };
00127 enum localEncKeyFieldIdxs {
00128     #define ENUM_AUTO(ENUM_AUTO) LEK_ ## ENUM_AUTO ## _IDX,
00129     ARM_UC_MM_MFST_CRYPT_LOCAL_ID_FIELDS
00130     #undef ENUM_AUTO
00131 };
00132 
00133 int ARM_UC_mmGetLocalIDAndKey(manifest_firmware_info_t* info, arm_uc_buffer_t* mfst_fwref)
00134 {
00135     arm_uc_buffer_t buffers[ARRAY_SIZE(localEncKeyFields)];
00136     int rc = ARM_UC_mmDERParseTree(arm_uc_mmManifestFirmwareDescription,
00137                           mfst_fwref,
00138                           ARRAY_SIZE(localEncKeyFields),
00139                           localEncKeyFields,
00140                           buffers);
00141     if (rc == 0)
00142     {
00143         // Found local key ID and encrypted key
00144         info->cipherMode = ARM_UC_MM_CIPHERMODE_PSK;
00145         // TODO: Handle non-enum format
00146         uint32_t format = ARM_UC_mmDerBuf2Uint(&buffers[LEK_ARM_UC_MM_DER_MFST_FW_FMT_ENUM_IDX]);
00147         memset(&info->format, 0, sizeof(info->format));
00148         info->format.words[RFC_4122_WORDS-1] = htobe(format);
00149         ARM_UC_buffer_shallow_copy(&info->initVector, &buffers[LEK_ARM_UC_MM_DER_MFST_FW_CRYPT_IV_IDX]);
00150         ARM_UC_buffer_shallow_copy(&info->psk.keyID, &buffers[LEK_ARM_UC_MM_DER_MFST_FW_FMT_ENUM_IDX]);
00151         ARM_UC_buffer_shallow_copy(&info->psk.cipherKey, &buffers[LEK_ARM_UC_MM_DER_MFST_FW_CRYPT_KEY_CIPHERKEY_IDX]);
00152 
00153         ARM_UC_buffer_shallow_copy(&info->strgId, &buffers[LEK_ARM_UC_MM_DER_MFST_FW_STRG_ID_IDX]);
00154         ARM_UC_buffer_shallow_copy(&info->hash, &buffers[LEK_ARM_UC_MM_DER_MFST_FW_RSRC_REF_HASH_IDX]);
00155         ARM_UC_buffer_shallow_copy(&info->uri, &buffers[LEK_ARM_UC_MM_DER_MFST_FW_RSRC_REF_URL_IDX]);
00156         info->size = ARM_UC_mmDerBuf2Uint(&buffers[LEK_ARM_UC_MM_DER_MFST_FW_RSRC_REF_SIZE_IDX]);
00157     }
00158     return rc;
00159 }
00160 
00161 
00162 #define ARM_UC_MM_MFST_CRYPT_CERT_KEY_FIELDS \
00163     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_FMT_ENUM)\
00164     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_CRYPT_IV)\
00165     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_CRYPT_ID_CERT_FINGERPRINT)\
00166     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_CRYPT_ID_CERT_URL)\
00167     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_CRYPT_KEY_CIPHERKEY)\
00168     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_STRG_ID)\
00169     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_RSRC_REF_HASH)\
00170     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_RSRC_REF_URL)\
00171     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_RSRC_REF_SIZE)\
00172 
00173 static const int32_t certEncKeyFields [] = {
00174     #define ENUM_AUTO(ENUM_AUTO) ENUM_AUTO,
00175     ARM_UC_MM_MFST_CRYPT_CERT_KEY_FIELDS
00176     #undef ENUM_AUTO
00177 };
00178 
00179 enum certEncKeyFieldIdxs {
00180     #define ENUM_AUTO(ENUM_AUTO) CEK_ ## ENUM_AUTO ## _IDX,
00181     ARM_UC_MM_MFST_CRYPT_CERT_KEY_FIELDS
00182     #undef ENUM_AUTO
00183 };
00184 
00185 int ARM_UC_mmGetCertAndKey(manifest_firmware_info_t* info, arm_uc_buffer_t* mfst_fwref)
00186 {
00187     arm_uc_buffer_t buffers[ARRAY_SIZE(certEncKeyFields)];
00188     int rc = ARM_UC_mmDERParseTree(arm_uc_mmManifestFirmwareDescription,
00189                           mfst_fwref,
00190                           ARRAY_SIZE(certEncKeyFields),
00191                           certEncKeyFields,
00192                           buffers);
00193     if (rc == 0)
00194     {
00195         info->cipherMode = ARM_UC_MM_CIPHERMODE_CERT_CIPHERKEY;
00196         // TODO: Handle non-enum format
00197         uint32_t format = ARM_UC_mmDerBuf2Uint(&buffers[CEK_ARM_UC_MM_DER_MFST_FW_FMT_ENUM_IDX]);
00198         memset(&info->format, 0, sizeof(info->format));
00199         info->format.words[RFC_4122_WORDS-1] = htobe(format);
00200         ARM_UC_buffer_shallow_copy(&info->initVector, &buffers[CEK_ARM_UC_MM_DER_MFST_FW_CRYPT_IV_IDX]);
00201 
00202         ARM_UC_buffer_shallow_copy(&info->certCK.certFingerPrint, &buffers[CEK_ARM_UC_MM_DER_MFST_FW_CRYPT_ID_CERT_FINGERPRINT_IDX]);
00203         ARM_UC_buffer_shallow_copy(&info->certCK.certURL, &buffers[CEK_ARM_UC_MM_DER_MFST_FW_CRYPT_ID_CERT_URL_IDX]);
00204         ARM_UC_buffer_shallow_copy(&info->certCK.cipherKey, &buffers[CEK_ARM_UC_MM_DER_MFST_FW_CRYPT_KEY_CIPHERKEY_IDX]);
00205 
00206         ARM_UC_buffer_shallow_copy(&info->strgId, &buffers[CEK_ARM_UC_MM_DER_MFST_FW_STRG_ID_IDX]);
00207         ARM_UC_buffer_shallow_copy(&info->hash, &buffers[CEK_ARM_UC_MM_DER_MFST_FW_RSRC_REF_HASH_IDX]);
00208         ARM_UC_buffer_shallow_copy(&info->uri, &buffers[CEK_ARM_UC_MM_DER_MFST_FW_RSRC_REF_URL_IDX]);
00209         info->size = ARM_UC_mmDerBuf2Uint(&buffers[CEK_ARM_UC_MM_DER_MFST_FW_RSRC_REF_SIZE_IDX]);
00210     }
00211     return rc;
00212 }
00213 
00214 
00215 #define ARM_UC_MM_MFST_CRYPT_CERT_KEYTABLE_FIELDS \
00216     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_FMT_ENUM)\
00217     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_CRYPT_IV)\
00218     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_CRYPT_ID_CERT_FINGERPRINT)\
00219     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_CRYPT_ID_CERT_URL)\
00220     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_CRYPT_KEY_KEYTABLE_REF)\
00221     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_STRG_ID)\
00222     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_RSRC_REF_HASH)\
00223     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_RSRC_REF_URL)\
00224     ENUM_AUTO(ARM_UC_MM_DER_MFST_FW_RSRC_REF_SIZE)\
00225 
00226 static const int32_t certKeyTableFields [] = {
00227     #define ENUM_AUTO(ENUM_AUTO) ENUM_AUTO,
00228     ARM_UC_MM_MFST_CRYPT_CERT_KEYTABLE_FIELDS
00229     #undef ENUM_AUTO
00230 };
00231 
00232 enum certKeyTableFieldIdxs {
00233     #define ENUM_AUTO(ENUM_AUTO) CKT_ ## ENUM_AUTO ## _IDX,
00234     ARM_UC_MM_MFST_CRYPT_CERT_KEYTABLE_FIELDS
00235     #undef ENUM_AUTO
00236 };
00237 
00238 int ARM_UC_mmGetCertAndKeyTable(manifest_firmware_info_t* info, arm_uc_buffer_t* mfst_fwref)
00239 {
00240     arm_uc_buffer_t buffers[ARRAY_SIZE(certKeyTableFields)];
00241     int rc = ARM_UC_mmDERParseTree(arm_uc_mmManifestFirmwareDescription,
00242                           mfst_fwref,
00243                           ARRAY_SIZE(certKeyTableFields),
00244                           certKeyTableFields,
00245                           buffers);
00246     if (rc == 0)
00247     {
00248         info->cipherMode = ARM_UC_MM_CIPHERMODE_CERT_KEYTABLE;
00249         // TODO: Handle non-enum format
00250         uint32_t format = ARM_UC_mmDerBuf2Uint(&buffers[CKT_ARM_UC_MM_DER_MFST_FW_FMT_ENUM_IDX]);
00251         memset(&info->format, 0, sizeof(info->format));
00252         info->format.words[RFC_4122_WORDS-1] = htobe(format);
00253         ARM_UC_buffer_shallow_copy(&info->initVector, &buffers[CKT_ARM_UC_MM_DER_MFST_FW_CRYPT_IV_IDX]);
00254 
00255         ARM_UC_buffer_shallow_copy(&info->certKT.certFingerPrint, &buffers[CKT_ARM_UC_MM_DER_MFST_FW_CRYPT_ID_CERT_FINGERPRINT_IDX]);
00256         ARM_UC_buffer_shallow_copy(&info->certKT.certURL, &buffers[CKT_ARM_UC_MM_DER_MFST_FW_CRYPT_ID_CERT_URL_IDX]);
00257         ARM_UC_buffer_shallow_copy(&info->certKT.keyTableURL, &buffers[CKT_ARM_UC_MM_DER_MFST_FW_CRYPT_KEY_KEYTABLE_REF_IDX]);
00258 
00259         ARM_UC_buffer_shallow_copy(&info->strgId, &buffers[CKT_ARM_UC_MM_DER_MFST_FW_STRG_ID_IDX]);
00260         ARM_UC_buffer_shallow_copy(&info->hash, &buffers[CKT_ARM_UC_MM_DER_MFST_FW_RSRC_REF_HASH_IDX]);
00261         ARM_UC_buffer_shallow_copy(&info->uri, &buffers[CKT_ARM_UC_MM_DER_MFST_FW_RSRC_REF_URL_IDX]);
00262         info->size = ARM_UC_mmDerBuf2Uint(&buffers[CKT_ARM_UC_MM_DER_MFST_FW_RSRC_REF_SIZE_IDX]);
00263     }
00264     return rc;
00265 }
00266 
00267 arm_uc_error_t ARM_UC_mmFetchFirmwareInfoFSM(uint32_t event)
00268 {
00269     arm_uc_error_t err = {MFST_ERR_NONE};
00270     if (arm_uc_mmPersistentContext.ctx == NULL || *arm_uc_mmPersistentContext.ctx == NULL)
00271     {
00272         return (arm_uc_error_t){MFST_ERR_NULL_PTR};
00273     }
00274     struct arm_uc_mm_fw_context_t* ctx = &(*arm_uc_mmPersistentContext.ctx)->getFw;
00275     if (ctx->info == NULL)
00276     {
00277         return (arm_uc_error_t){MFST_ERR_NULL_PTR};
00278     }
00279     ARM_UC_MM_FSM_HELPER_START(*ctx, ARM_UC_mmFwState2Str) {
00280         case ARM_UC_MM_FW_STATE_IDLE:
00281             err = (arm_uc_error_t){MFST_ERR_NONE};
00282             break;
00283         case ARM_UC_MM_FW_STATE_BEGIN:
00284         {
00285             // If there is no manifest storage, assume it is still present in the input buffer
00286             ctx->state = ARM_UC_MM_FW_STATE_READ_URI;
00287             ARM_UC_MM_SET_BUFFER(ctx->current_data, ctx->info->manifestBuffer);
00288             ctx->current_data.size = ctx->info->manifestSize;
00289             break;
00290         }
00291         case ARM_UC_MM_FW_STATE_READ_URI:
00292         {
00293             // Get the encryption mode and the firmware info block.
00294             const int32_t fieldIDs [] = {ARM_UC_MM_DER_MFST_ENC_ENUM, ARM_UC_MM_DER_MFST_FIRMWARE};
00295             arm_uc_buffer_t buffers [sizeof(fieldIDs)/sizeof(fieldIDs[0])];
00296             int rc = ARM_UC_mmDERGetSignedResourceValues(
00297                     &ctx->current_data,
00298                     sizeof(fieldIDs)/sizeof(fieldIDs[0]),
00299                     fieldIDs,
00300                     buffers);
00301             if (rc < 0)
00302             {
00303                 err.code = MFST_ERR_DER_FORMAT;
00304                 break;
00305             }
00306             else if (rc > 0)
00307             {
00308                 // in storage mode, firmware must be supplied.
00309                 err.code = MFST_ERR_EMPTY_FIELD;
00310                 break;
00311             }
00312             arm_uc_buffer_t fwBuf;
00313             ARM_UC_buffer_shallow_copy(&fwBuf, &buffers[1]);
00314 
00315             // Store timestamp
00316             ARM_UC_MM_SET_BUFFER(ctx->current_data, ctx->info->manifestBuffer);
00317             ctx->current_data.size = ctx->info->manifestSize;
00318             err = ARM_UC_mmGetTimestamp(&ctx->current_data, &ctx->info->timestamp);
00319             if (err.error != 0)
00320             {
00321                 break;
00322             }
00323 
00324             ctx->info->cipherMode = ARM_UC_MM_CIPHERMODE_NONE;
00325             // Found an encryption mode and firmware!
00326             uint32_t cryptoMode = ARM_UC_mmDerBuf2Uint(&buffers[0]);
00327             if (!ARM_UC_mmGetCryptoFlags(cryptoMode).aes)
00328             {
00329                 // Encryption not in use. Skip key, ID, and IV extraction.
00330                 rc = ARM_UC_mmGetImageRef(ctx->info, &fwBuf);
00331                 if (rc == 0)
00332                 {
00333                     ctx->state = ARM_UC_MM_FW_STATE_NOTIFY;
00334                     err.code = MFST_ERR_NONE;
00335                 }
00336                 else
00337                 {
00338                     err.code = MFST_ERR_DER_FORMAT;
00339                 }
00340                 break;
00341             }
00342             // There are three possible combinations of encryption info:
00343             // local key ID & encrypted key
00344             rc = ARM_UC_mmGetLocalIDAndKey(ctx->info, &fwBuf);
00345             if (!rc) {
00346                 ctx->state = ARM_UC_MM_FW_STATE_NOTIFY;
00347                 err.code = MFST_ERR_NONE;
00348                 break;
00349             }
00350             // Certificate and encrypted key
00351             rc = ARM_UC_mmGetCertAndKey(ctx->info, &fwBuf);
00352             if (!rc) {
00353                 ctx->state = ARM_UC_MM_FW_STATE_NOTIFY;
00354                 err.code = MFST_ERR_NONE;
00355                 break;
00356             }
00357             // Certificate and key table reference
00358             rc = ARM_UC_mmGetCertAndKeyTable(ctx->info, &fwBuf);
00359             if (!rc) {
00360                 ctx->state = ARM_UC_MM_FW_STATE_NOTIFY;
00361                 err.code = MFST_ERR_NONE;
00362                 break;
00363             }
00364 
00365             break;
00366         }
00367         case ARM_UC_MM_FW_STATE_GET_FW_REF:
00368 
00369             // TODO: Ref only
00370         case ARM_UC_MM_FW_STATE_NOTIFY:
00371             ctx->state = ARM_UC_MM_FW_STATE_ROOT_NOTIFY_WAIT;
00372             err.code = MFST_ERR_PENDING;
00373             ARM_UC_PostCallback(&ctx->callbackStorage, arm_uc_mmPersistentContext.applicationEventHandler, ARM_UC_MM_RC_NEED_FW);
00374             break;
00375 
00376         case ARM_UC_MM_FW_STATE_ROOT_NOTIFY_WAIT:
00377             if (event == ARM_UC_MM_EVENT_BEGIN)
00378             {
00379                 err.code = MFST_ERR_NONE;
00380                 ctx->state = ARM_UC_MM_FW_STATE_DONE;
00381             }
00382             break;
00383         case ARM_UC_MM_FW_STATE_DONE:
00384             // NOTE: The outer FSM will send the "done" message.
00385             ctx->state = ARM_UC_MM_FW_STATE_IDLE;
00386             break;
00387         case ARM_UC_MM_FW_STATE_INVALID:
00388         default:
00389             err = (arm_uc_error_t){MFST_ERR_INVALID_STATE};
00390             break;
00391     } ARM_UC_MM_FSM_HELPER_FINISH(*ctx);
00392     return err;
00393 }