summaryrefslogtreecommitdiffstats
path: root/engines/ccgost/gost2001_keyx.c
diff options
context:
space:
mode:
authorDr. Stephen Henson <steve@openssl.org>2007-10-26 12:06:36 +0000
committerDr. Stephen Henson <steve@openssl.org>2007-10-26 12:06:36 +0000
commit0e1dba934fa53e9736e9156b9e25bd1010290149 (patch)
treee52e12fa1147b634c215263e93d77c8c9830b39b /engines/ccgost/gost2001_keyx.c
parent11d01d371f67a9cacfeccb1078669c595d65002f (diff)
1. Changes for s_client.c to make it return non-zero exit code in case
of handshake failure 2. Changes to x509_certificate_type function (crypto/x509/x509type.c) to make it recognize GOST certificates as EVP_PKT_SIGN|EVP_PKT_EXCH (required for s3_srvr to accept GOST client certificates). 3. Changes to EVP - adding of function EVP_PKEY_CTX_get0_peerkey - Make function EVP_PKEY_derive_set_peerkey work for context with ENCRYPT operation, because we use peerkey field in the context to pass non-ephemeral secret key to GOST encrypt operation. - added EVP_PKEY_CTRL_SET_IV control command. It is really GOST-specific, but it is used in SSL code, so it has to go in some header file, available during libssl compilation 4. Fix to HMAC to avoid call of OPENSSL_cleanse on undefined data 5. Include des.h if KSSL_DEBUG is defined into some libssl files, to make debugging output which depends on constants defined there, work and other KSSL_DEBUG output fixes 6. Declaration of real GOST ciphersuites, two authentication methods SSL_aGOST94 and SSL_aGOST2001 and one key exchange method SSL_kGOST 7. Implementation of these methods. 8. Support for sending unsolicited serverhello extension if GOST ciphersuite is selected. It is require for interoperability with CryptoPro CSP 3.0 and 3.6 and controlled by SSL_OP_CRYPTOPRO_TLSEXT_BUG constant. This constant is added to SSL_OP_ALL, because it does nothing, if non-GOST ciphersuite is selected, and all implementation of GOST include compatibility with CryptoPro. 9. Support for CertificateVerify message without length field. It is another CryptoPro bug, but support is made unconditional, because it does no harm for draft-conforming implementation. 10. In tls1_mac extra copy of stream mac context is no more done. When I've written currently commited code I haven't read EVP_DigestSignFinal manual carefully enough and haven't noticed that it does an internal digest ctx copying. This implementation was tested against 1. CryptoPro CSP 3.6 client and server 2. Cryptopro CSP 3.0 server
Diffstat (limited to 'engines/ccgost/gost2001_keyx.c')
-rw-r--r--engines/ccgost/gost2001_keyx.c431
1 files changed, 149 insertions, 282 deletions
diff --git a/engines/ccgost/gost2001_keyx.c b/engines/ccgost/gost2001_keyx.c
index 3cef5f2e38..013659aa23 100644
--- a/engines/ccgost/gost2001_keyx.c
+++ b/engines/ccgost/gost2001_keyx.c
@@ -18,194 +18,7 @@
#include "gost_lcl.h"
#include "gost2001_keyx.h"
-/* Transform ECDH shared key into little endian as required by Cryptocom
- * key exchange */
-static void *make_key_le(const void *in, size_t inlen, void *out, size_t *outlen)
- {
- const char* inbuf= in;
- char* outbuf= out;
- int i;
- if (*outlen < inlen)
- {
- return NULL;
- }
- for (i=0;i<inlen;i++)
- {
- outbuf[inlen-1-i]=inbuf[i];
- }
- *outlen = inlen;
- return out;
- }
-/* Create gost 2001 ephemeral key with same parameters as peer key */
-static EC_KEY *make_ec_ephemeral_key(EC_KEY *peer_key,BIGNUM *seckey)
- {
- EC_KEY *out = EC_KEY_new();
- EC_KEY_copy(out,peer_key);
- EC_KEY_set_private_key(out,seckey);
- gost2001_compute_public(out);
- return out;
- }
-/* Packs GOST elliptic curve key into EVP_PKEY setting same parameters
- * as in passed pubkey
- */
-static EVP_PKEY *ec_ephemeral_key_to_EVP(EVP_PKEY *pubk,int type,EC_KEY *ephemeral)
- {
- EVP_PKEY *newkey;
- newkey = EVP_PKEY_new();
- EVP_PKEY_assign(newkey,type,ephemeral);
- return newkey;
- }
-
-/*
- * EVP_PKEY_METHOD callback encrypt
- * Implementation of GOST2001 key transport, cryptocom variation
- */
-
-int pkey_GOST01cc_encrypt (EVP_PKEY_CTX *pctx,unsigned char *out,
- size_t *out_len, const unsigned char *key,size_t key_len)
- {
- EVP_PKEY *pubk = EVP_PKEY_CTX_get0_pkey(pctx);
- struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(pctx);
- GOST_KEY_TRANSPORT *gkt = NULL;
- int ret=0;
- const struct gost_cipher_info *cipher_info;
- gost_ctx ctx;
- EC_KEY *ephemeral=NULL;
- const EC_POINT *pub_key_point=NULL;
- unsigned char shared_key[32],encrypted_key[32],hmac[4],
- iv[8]={0,0,0,0,0,0,0,0};
- ephemeral = make_ec_ephemeral_key(EVP_PKEY_get0(pubk), gost_get_priv_key(data->eph_seckey));
- if (!ephemeral) goto err;
- /* compute shared key */
- pub_key_point=EC_KEY_get0_public_key(EVP_PKEY_get0(pubk));
- if (!ECDH_compute_key(shared_key,32,pub_key_point,ephemeral,make_key_le))
- {
- GOSTerr(GOST_F_PKEY_GOST01CC_ENCRYPT,GOST_R_ERROR_COMPUTING_SHARED_KEY);
- goto err;
- }
- /* encrypt session key */
- cipher_info = get_encryption_params(NULL);
- gost_init(&ctx, cipher_info->sblock);
- gost_key(&ctx,shared_key);
- encrypt_cryptocom_key(key,key_len,encrypted_key,&ctx);
- /* compute hmac of session key */
- if (!gost_mac(&ctx,32,key,32,hmac))
- {
- GOSTerr(GOST_F_PKEY_GOST01CC_ENCRYPT,GOST_R_ERROR_COMPUTING_MAC);
- return -1;
- }
- gkt = GOST_KEY_TRANSPORT_new();
- if (!gkt)
- {
- GOSTerr(GOST_F_PKEY_GOST01CC_ENCRYPT,GOST_R_NO_MEMORY);
- return -1;
- }
- /* Store IV which is always zero in our case */
- if (!ASN1_OCTET_STRING_set(gkt->key_agreement_info->eph_iv,iv,8))
- {
- GOSTerr(GOST_F_PKEY_GOST01CC_ENCRYPT,GOST_R_ERROR_STORING_IV);
- goto err;
- }
- if (!ASN1_OCTET_STRING_set(gkt->key_info->imit,hmac,4))
- {
- GOSTerr(GOST_F_PKEY_GOST01CC_ENCRYPT,GOST_R_ERROR_STORING_MAC);
- goto err;
- }
- if (!ASN1_OCTET_STRING_set(gkt->key_info->encrypted_key,encrypted_key,32))
- {
- GOSTerr(GOST_F_PKEY_GOST01CC_ENCRYPT,GOST_R_ERROR_STORING_ENCRYPTED_KEY);
- goto err;
- }
-
- if (!X509_PUBKEY_set(&gkt->key_agreement_info->ephem_key,data->eph_seckey))
- {
- GOSTerr(GOST_F_PKEY_GOST01CC_ENCRYPT,GOST_R_CANNOT_PACK_EPHEMERAL_KEY);
- goto err;
- }
- ASN1_OBJECT_free(gkt->key_agreement_info->cipher);
- gkt->key_agreement_info->cipher = OBJ_nid2obj(cipher_info->nid);
- if ((*out_len = i2d_GOST_KEY_TRANSPORT(gkt,&out))>0) ret = 1;
- ;
- err:
- if (gkt) GOST_KEY_TRANSPORT_free(gkt);
- return ret;
- }
-/*
- * EVP_PKEY_METHOD callback decrypt
- * Implementation of GOST2001 key transport, cryptocom variation
- */
-int pkey_GOST01cc_decrypt (EVP_PKEY_CTX *pctx, unsigned char *key, size_t *key_len, const unsigned char *in, size_t in_len)
- {
- /* Form DH params from compute shared key */
- EVP_PKEY *priv=EVP_PKEY_CTX_get0_pkey(pctx);
- GOST_KEY_TRANSPORT *gkt = NULL;
- const unsigned char *p=in;
- unsigned char shared_key[32];
- unsigned char hmac[4],hmac_comp[4];
- unsigned char iv[8];
- int i;
- const struct gost_cipher_info *cipher_info;
- gost_ctx ctx;
- const EC_POINT *pub_key_point;
- EVP_PKEY *eph_key;
-
- if (!key)
- {
- *key_len = 32;
- return 1;
- }
- /* Parse passed octet string and find out public key, iv and HMAC*/
- gkt = d2i_GOST_KEY_TRANSPORT(NULL,(const unsigned char **)&p,
- in_len);
- if (!gkt)
- {
- GOSTerr(GOST_F_PKEY_GOST01CC_DECRYPT,GOST_R_ERROR_PARSING_KEY_TRANSPORT_INFO);
- return 0;
- }
- eph_key = X509_PUBKEY_get(gkt->key_agreement_info->ephem_key);
- /* Initialization vector is really ignored here */
- OPENSSL_assert(gkt->key_agreement_info->eph_iv->length==8);
- memcpy(iv,gkt->key_agreement_info->eph_iv->data,8);
- /* HMAC should be computed and checked */
- OPENSSL_assert(gkt->key_info->imit->length==4);
- memcpy(hmac,gkt->key_info->imit->data,4);
- /* Compute shared key */
- pub_key_point=EC_KEY_get0_public_key(EVP_PKEY_get0(eph_key));
- i=ECDH_compute_key(shared_key,32,pub_key_point,EVP_PKEY_get0(priv),make_key_le);
- EVP_PKEY_free(eph_key);
- if (!i)
- {
- GOSTerr(GOST_F_PKEY_GOST01CC_DECRYPT,GOST_R_ERROR_COMPUTING_SHARED_KEY);
- GOST_KEY_TRANSPORT_free(gkt);
- return 0;
- }
- /* Decrypt session key */
- cipher_info = get_encryption_params(gkt->key_agreement_info->cipher);
- gost_init(&ctx, cipher_info->sblock);
- gost_key(&ctx,shared_key);
-
- if (!decrypt_cryptocom_key(key,*key_len,gkt->key_info->encrypted_key->data,
- gkt->key_info->encrypted_key->length, &ctx))
- {
- GOST_KEY_TRANSPORT_free(gkt);
- return 0;
- }
- GOST_KEY_TRANSPORT_free(gkt);
- /* check HMAC of session key*/
- if (!gost_mac(&ctx,32,key,32,hmac_comp))
- {
- GOSTerr(GOST_F_PKEY_GOST01CC_DECRYPT,GOST_R_ERROR_COMPUTING_MAC);
- return 0;
- }
- /* HMAC of session key is not correct */
- if (memcmp(hmac,hmac_comp,4)!=0)
- {
- GOSTerr(GOST_F_PKEY_GOST01CC_DECRYPT,GOST_R_SESSION_KEY_MAC_DOES_NOT_MATCH);
- return 0;
- }
- return 1;
- }
/* Implementation of CryptoPro VKO 34.10-2001 algorithm */
static int VKO_compute_key(unsigned char *shared_key,size_t shared_key_size,const EC_POINT *pub_key,EC_KEY *priv_key,const unsigned char *ukm)
@@ -254,110 +67,191 @@ static int VKO_compute_key(unsigned char *shared_key,size_t shared_key_size,cons
return 32;
}
+
+/*
+ * EVP_PKEY_METHOD callback derive. Implements VKO R 34.10-2001
+ * algorithm
+ */
+int pkey_gost2001_derive(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen)
+{
+ /* Public key of peer in the ctx field peerkey
+ * Our private key in the ctx pkey
+ * ukm is in the algorithm specific context data
+ */
+ EVP_PKEY *my_key = EVP_PKEY_CTX_get0_pkey(ctx);
+ EVP_PKEY *peer_key = EVP_PKEY_CTX_get0_peerkey(ctx);
+ struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(ctx);
+
+ if (!data->shared_ukm) {
+ GOSTerr(GOST_F_PKEY_GOST2001_DERIVE, GOST_R_UKM_NOT_SET);
+ return 0;
+ }
+
+ if (key == NULL) {
+ *keylen = 32;
+ return 32;
+ }
+
+ *keylen=VKO_compute_key(key, 32, EC_KEY_get0_public_key(EVP_PKEY_get0(peer_key)),
+ (EC_KEY *)EVP_PKEY_get0(my_key),data->shared_ukm);
+ return 1;
+}
+
+
+
+
+/*
+ * EVP_PKEY_METHOD callback encrypt
+ * Implementation of GOST2001 key transport, cryptocom variation
+ */
/* Generates ephemeral key based on pubk algorithm
* computes shared key using VKO and returns filled up
* GOST_KEY_TRANSPORT structure
*/
-/* Public, because it would be needed in SSL implementation */
-GOST_KEY_TRANSPORT *make_rfc4490_keytransport_2001(EVP_PKEY *pubk,BIGNUM *eph_key,
- const unsigned char *key,size_t keylen, unsigned char *ukm,
- size_t ukm_len)
- {
+/*
+ * EVP_PKEY_METHOD callback encrypt
+ * Implementation of GOST2001 key transport, cryptopo variation
+ */
+
+int pkey_GOST01cp_encrypt (EVP_PKEY_CTX *pctx, unsigned char *out, size_t *out_len, const unsigned char *key,size_t key_len)
+ {
+ GOST_KEY_TRANSPORT *gkt=NULL;
+ EVP_PKEY *pubk = EVP_PKEY_CTX_get0_pkey(pctx);
+ struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(pctx);
const struct gost_cipher_info *param=get_encryption_params(NULL);
- EC_KEY *ephemeral = NULL;
- GOST_KEY_TRANSPORT *gkt=NULL;
- const EC_POINT *pub_key_point = EC_KEY_get0_public_key(EVP_PKEY_get0(pubk));
- unsigned char shared_key[32],crypted_key[44];
- gost_ctx ctx;
- EVP_PKEY *newkey=NULL;
-
- /* Do not use vizir cipher parameters with cryptopro */
+ unsigned char ukm[8], shared_key[32], crypted_key[44];
+ int ret=0;
+ int key_is_ephemeral=1;
+ gost_ctx cctx;
+ EVP_PKEY *sec_key=EVP_PKEY_CTX_get0_peerkey(pctx);
+ if (data->shared_ukm)
+ {
+ memcpy(ukm, data->shared_ukm,8);
+ }
+ else if (out)
+ {
+
+ if (RAND_bytes(ukm,8)<=0)
+ {
+ GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT,
+ GOST_R_RANDOM_GENERATOR_FAILURE);
+ return 0;
+ }
+ }
+ /* Check for private key in the peer_key of context */
+ if (sec_key)
+ {
+ key_is_ephemeral=0;
+ if (!gost_get0_priv_key(sec_key))
+ {
+ GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT,
+ GOST_R_NO_PRIVATE_PART_OF_NON_EPHEMERAL_KEYPAIR);
+ goto err;
+ }
+ }
+ else
+ {
+ key_is_ephemeral=1;
+ if (out)
+ {
+ sec_key = EVP_PKEY_new();
+ EVP_PKEY_assign(sec_key,EVP_PKEY_base_id(pubk),EC_KEY_new());
+ EVP_PKEY_copy_parameters(sec_key,pubk);
+ if (!gost2001_keygen(EVP_PKEY_get0(sec_key)))
+ {
+ goto err;
+ }
+ }
+ }
if (!get_gost_engine_param(GOST_PARAM_CRYPT_PARAMS) && param == gost_cipher_list)
{
param= gost_cipher_list+1;
}
- ephemeral = make_ec_ephemeral_key(EVP_PKEY_get0(pubk),eph_key);
- VKO_compute_key(shared_key,32,pub_key_point,ephemeral,ukm);
- gost_init(&ctx,param->sblock);
- keyWrapCryptoPro(&ctx,shared_key,ukm,key,crypted_key);
+ if (out)
+ {
+ VKO_compute_key(shared_key,32,EC_KEY_get0_public_key(EVP_PKEY_get0(pubk)),EVP_PKEY_get0(sec_key),ukm);
+ gost_init(&cctx,param->sblock);
+ keyWrapCryptoPro(&cctx,shared_key,ukm,key,crypted_key);
+ }
gkt = GOST_KEY_TRANSPORT_new();
if (!gkt)
{
- goto memerr;
+ goto err;
}
if(!ASN1_OCTET_STRING_set(gkt->key_agreement_info->eph_iv,
ukm,8))
{
- goto memerr;
+ goto err;
}
if (!ASN1_OCTET_STRING_set(gkt->key_info->imit,crypted_key+40,4))
{
- goto memerr;
+ goto err;
}
if (!ASN1_OCTET_STRING_set(gkt->key_info->encrypted_key,crypted_key+8,32))
{
- goto memerr;
- }
- newkey = ec_ephemeral_key_to_EVP(pubk,NID_id_GostR3410_2001,ephemeral);
- if (!X509_PUBKEY_set(&gkt->key_agreement_info->ephem_key,newkey))
- {
- GOSTerr(GOST_F_MAKE_RFC4490_KEYTRANSPORT_2001,GOST_R_CANNOT_PACK_EPHEMERAL_KEY);
goto err;
- }
+ }
+ if (key_is_ephemeral) {
+ if (!X509_PUBKEY_set(&gkt->key_agreement_info->ephem_key,out?sec_key:pubk))
+ {
+ GOSTerr(GOST_F_MAKE_RFC4490_KEYTRANSPORT_2001,
+ GOST_R_CANNOT_PACK_EPHEMERAL_KEY);
+ goto err;
+ }
+ }
ASN1_OBJECT_free(gkt->key_agreement_info->cipher);
gkt->key_agreement_info->cipher = OBJ_nid2obj(param->nid);
- EVP_PKEY_free(newkey);
- return gkt;
- memerr:
- GOSTerr(GOST_F_MAKE_RFC4490_KEYTRANSPORT_2001,
- GOST_R_MALLOC_FAILURE);
- err:
- GOST_KEY_TRANSPORT_free(gkt);
- return NULL;
- }
-
-/*
- * EVP_PKEY_METHOD callback encrypt
- * Implementation of GOST2001 key transport, cryptopo variation
- */
-
-int pkey_GOST01cp_encrypt (EVP_PKEY_CTX *pctx, unsigned char *out, size_t *out_len, const unsigned char *key,size_t key_len)
- {
- GOST_KEY_TRANSPORT *gkt=NULL;
- EVP_PKEY *pubk = EVP_PKEY_CTX_get0_pkey(pctx);
- struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(pctx);
- unsigned char ukm[8];
- int ret=0;
- if (RAND_bytes(ukm,8)<=0)
- {
- GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT,
- GOST_R_RANDOM_GENERATOR_FAILURE);
- return 0;
- }
-
- if (!(gkt=make_rfc4490_keytransport_2001(pubk,gost_get_priv_key(data->eph_seckey),key, key_len,ukm,8)))
- {
- goto err;
- }
- if ((*out_len = i2d_GOST_KEY_TRANSPORT(gkt,&out))>0) ret =1;
+ if (key_is_ephemeral && sec_key) EVP_PKEY_free(sec_key);
+ if ((*out_len = i2d_GOST_KEY_TRANSPORT(gkt,out?&out:NULL))>0) ret =1;
GOST_KEY_TRANSPORT_free(gkt);
return ret;
err:
+ if (key_is_ephemeral && sec_key) EVP_PKEY_free(sec_key);
GOST_KEY_TRANSPORT_free(gkt);
return -1;
}
-/* Public, because it would be needed in SSL implementation */
-int decrypt_rfc4490_shared_key_2001(EVP_PKEY *priv,GOST_KEY_TRANSPORT *gkt,
- unsigned char *key_buf,int key_buf_len)
+/*
+ * EVP_PKEY_METHOD callback decrypt
+ * Implementation of GOST2001 key transport, cryptopo variation
+ */
+int pkey_GOST01cp_decrypt (EVP_PKEY_CTX *pctx, unsigned char *key, size_t * key_len, const unsigned char *in, size_t in_len)
{
+ const unsigned char *p = in;
+ EVP_PKEY *priv = EVP_PKEY_CTX_get0_pkey(pctx);
+ GOST_KEY_TRANSPORT *gkt = NULL;
+ int ret=0;
unsigned char wrappedKey[44];
unsigned char sharedKey[32];
gost_ctx ctx;
const struct gost_cipher_info *param=NULL;
EVP_PKEY *eph_key=NULL;
-
+
+ if (!key)
+ {
+ *key_len = 32;
+ return 1;
+ }
+ gkt = d2i_GOST_KEY_TRANSPORT(NULL,(const unsigned char **)&p,
+ in_len);
+ if (!gkt)
+ {
+ GOSTerr(GOST_F_PKCS7_GOST94CP_KEY_TRANSPORT_DECRYPT,GOST_R_ERROR_PARSING_KEY_TRANSPORT_INFO);
+ return -1;
+ }
+
eph_key = X509_PUBKEY_get(gkt->key_agreement_info->ephem_key);
+ if (!eph_key) {
+ eph_key = EVP_PKEY_CTX_get0_peerkey(pctx);
+ if (! eph_key) {
+ GOSTerr(GOST_F_PKEY_GOST94CP_DECRYPT,
+ GOST_R_NO_PEER_KEY);
+ goto err;
+ }
+ /* Increment reference count of peer key */
+ CRYPTO_add(&(eph_key->references),1 ,CRYPTO_LOCK_EVP_PKEY);
+ }
+
param = get_encryption_params(gkt->key_agreement_info->cipher);
gost_init(&ctx,param->sblock);
OPENSSL_assert(gkt->key_agreement_info->eph_iv->length==8);
@@ -368,7 +262,7 @@ int decrypt_rfc4490_shared_key_2001(EVP_PKEY *priv,GOST_KEY_TRANSPORT *gkt,
memcpy(wrappedKey+40,gkt->key_info->imit->data,4);
VKO_compute_key(sharedKey,32,EC_KEY_get0_public_key(EVP_PKEY_get0(eph_key)),
EVP_PKEY_get0(priv),wrappedKey);
- if (!keyUnwrapCryptoPro(&ctx,sharedKey,wrappedKey,key_buf))
+ if (!keyUnwrapCryptoPro(&ctx,sharedKey,wrappedKey,key))
{
GOSTerr(GOST_F_PKCS7_GOST94CP_KEY_TRANSPORT_DECRYPT,
GOST_R_ERROR_COMPUTING_SHARED_KEY);
@@ -376,35 +270,8 @@ int decrypt_rfc4490_shared_key_2001(EVP_PKEY *priv,GOST_KEY_TRANSPORT *gkt,
}
EVP_PKEY_free(eph_key);
- return 32;
- err:
- EVP_PKEY_free(eph_key);
- return -1;
- }
-/*
- * EVP_PKEY_METHOD callback decrypt
- * Implementation of GOST2001 key transport, cryptopo variation
- */
-int pkey_GOST01cp_decrypt (EVP_PKEY_CTX *pctx, unsigned char *key, size_t * key_len, const unsigned char *in, size_t in_len)
- {
- const unsigned char *p = in;
- EVP_PKEY *priv = EVP_PKEY_CTX_get0_pkey(pctx);
- GOST_KEY_TRANSPORT *gkt = NULL;
- int ret=0;
-
- if (!key)
- {
- *key_len = 32;
- return 1;
- }
- gkt = d2i_GOST_KEY_TRANSPORT(NULL,(const unsigned char **)&p,
- in_len);
- if (!gkt)
- {
- GOSTerr(GOST_F_PKCS7_GOST94CP_KEY_TRANSPORT_DECRYPT,GOST_R_ERROR_PARSING_KEY_TRANSPORT_INFO);
- return -1;
- }
- ret = decrypt_rfc4490_shared_key_2001(priv,gkt,key,*key_len);
GOST_KEY_TRANSPORT_free(gkt);
+ ret=1;
+err:
return ret;
}