/** @fn int soap_smd_update(struct soap *soap, struct soap_smd_data *data, const char *buf, size_t len) @brief Updates (signed) digest computation with message part. @param soap context @param[in,out] data smdevp engine context @param[in] buf contains message part @param[in] len of message part @return SOAP_OK or SOAP_SSL_ERROR */ int soap_smd_update(struct soap *soap, struct soap_smd_data *data, const char *buf, size_t len) { int ok = 1; if (!data->ctx) return soap_set_receiver_error(soap, "soap_smd_update() failed", "No context", SOAP_SSL_ERROR); DBGLOG(TEST, SOAP_MESSAGE(fdebug, "-- SMD Update alg=%x n=%lu (%p) --\n", data->alg, (unsigned long)len, data->ctx)); switch (data->alg & SOAP_SMD_ALGO) { case SOAP_SMD_HMAC: HMAC_Update((HMAC_CTX*)data->ctx, (const unsigned char*)buf, len); break; case SOAP_SMD_DGST: EVP_DigestUpdate((EVP_MD_CTX*)data->ctx, (const void*)buf, (unsigned int)len); break; case SOAP_SMD_SIGN: ok = EVP_SignUpdate((EVP_MD_CTX*)data->ctx, (const void*)buf, (unsigned int)len); break; case SOAP_SMD_VRFY: ok = EVP_VerifyUpdate((EVP_MD_CTX*)data->ctx, (const void*)buf, (unsigned int)len); break; } DBGMSG(TEST, buf, len); DBGLOG(TEST, SOAP_MESSAGE(fdebug, "\n--")); /* check and return */ return soap_smd_check(soap, data, ok, "soap_smd_update() failed"); }
/** @fn int soap_smd_final(struct soap *soap, struct soap_smd_data *data, char *buf, int *len) @brief Finalizes (signed) digest computation and returns digest or signature. @param soap context @param[in,out] data smdevp engine context @param[in] buf contains signature for verification (SOAP_SMD_VRFY algorithms) @param[out] buf is populated with the digest or signature @param[in] len points to length of signature to verify (SOAP_SMD_VRFY algorithms) @param[out] len points to length of stored digest or signature (pass NULL if you are not interested in this value) @return SOAP_OK or SOAP_SSL_ERROR */ int soap_smd_final(struct soap *soap, struct soap_smd_data *data, char *buf, int *len) { unsigned int n = 0; int ok = 1; if (!data->ctx) return soap_set_receiver_error(soap, "soap_smd_final() failed", "No context", SOAP_SSL_ERROR); if (buf) { /* finalize the digest or signature computation */ switch (data->alg & SOAP_SMD_ALGO) { case SOAP_SMD_HMAC: HMAC_Final((HMAC_CTX*)data->ctx, (unsigned char*)buf, &n); break; case SOAP_SMD_DGST: EVP_DigestFinal_ex((EVP_MD_CTX*)data->ctx, (unsigned char*)buf, &n); break; case SOAP_SMD_SIGN: ok = EVP_SignFinal((EVP_MD_CTX*)data->ctx, (unsigned char*)buf, &n, (EVP_PKEY*)data->key); break; case SOAP_SMD_VRFY: if (len) { n = (unsigned int)*len; ok = EVP_VerifyFinal((EVP_MD_CTX*)data->ctx, (unsigned char*)buf, n, (EVP_PKEY*)data->key); } else ok = 0; break; } DBGLOG(TEST, SOAP_MESSAGE(fdebug, "-- SMD Final alg=%x (%p) %d bytes--\n", data->alg, data->ctx, n)); DBGHEX(TEST, buf, n); DBGLOG(TEST, SOAP_MESSAGE(fdebug, "\n--")); /* pass back length of digest or signature produced */ if (len) *len = (int)n; } /* cleanup */ if ((data->alg & SOAP_SMD_ALGO) == SOAP_SMD_HMAC) HMAC_CTX_cleanup((HMAC_CTX*)data->ctx); else EVP_MD_CTX_cleanup((EVP_MD_CTX*)data->ctx); SOAP_FREE(soap, data->ctx); data->ctx = NULL; /* check and return */ return soap_smd_check(soap, data, ok, "soap_smd_final() failed"); }
/** @fn int soap_smd_check(struct soap *soap, struct soap_smd_data *data, int err, const char *msg) @brief Check result of init/update/final smdevp engine operations. @param soap context @param[in,out] data smdevp engine context @param[in] err EVP error value @param[in] msg error message @return SOAP_OK or SOAP_SSL_ERROR */ static int soap_smd_check(struct soap *soap, struct soap_smd_data *data, int err, const char *msg) { if (err <= 0) { unsigned long r; while ((r = ERR_get_error())) { ERR_error_string_n(r, soap->msgbuf, sizeof(soap->msgbuf)); DBGLOG(TEST, SOAP_MESSAGE(fdebug, "-- SMD Error (%d) %s: %s\n", err, msg, soap->msgbuf)); } if (data->ctx) { if ((data->alg & (SOAP_SMD_PASSTHRU-1)) == SOAP_SMD_HMAC_SHA1) HMAC_CTX_cleanup((HMAC_CTX*)data->ctx); else EVP_MD_CTX_cleanup((EVP_MD_CTX*)data->ctx); SOAP_FREE(soap, data->ctx); data->ctx = NULL; } return soap_set_receiver_error(soap, msg, soap->msgbuf, SOAP_SSL_ERROR); } return SOAP_OK; }
/** @fn int soap_mec_init(struct soap *soap, struct soap_mec_data *data, int alg, SOAP_MEC_KEY_TYPE *pkey, unsigned char *key, int *keylen) @brief Initialize mecevp engine state and create context for encryption/decryption algorithm using a private/public key or symmetric secret key. @param soap context @param[in,out] data mecevp engine context @param[in] alg encryption/decryption algorithm @param[in] pkey public/private key or NULL @param[in,out] key secret key or encrypted ephemeral secret key set with envelope encryption, or NULL @param[in,out] keylen secret key length @return SOAP_OK or SOAP_SSL_ERROR */ int soap_mec_init(struct soap *soap, struct soap_mec_data *data, int alg, SOAP_MEC_KEY_TYPE *pkey, unsigned char *key, int *keylen) { int ok = 1; DBGLOG(TEST, SOAP_MESSAGE(fdebug, "soap_mec_init()\n")); soap_ssl_init(); data->ctx = (EVP_CIPHER_CTX*)SOAP_MALLOC(soap, sizeof(EVP_CIPHER_CTX)); if (!data->ctx) return soap->error = SOAP_EOM; EVP_CIPHER_CTX_init(data->ctx); data->alg = alg; data->state = SOAP_MEC_STATE_NONE; if (alg & SOAP_MEC_DES_CBC) data->type = EVP_des_ede3_cbc(); /* triple DES CBC */ else if (alg & SOAP_MEC_AES128_CBC) data->type = EVP_get_cipherbyname("AES128"); else if (alg & SOAP_MEC_AES192_CBC) data->type = EVP_get_cipherbyname("AES192"); else if (alg & SOAP_MEC_AES256_CBC) data->type = EVP_get_cipherbyname("AES256"); else if (alg & SOAP_MEC_AES512_CBC) data->type = EVP_get_cipherbyname("AES512"); else data->type = EVP_enc_null(); data->buf = NULL; data->rest = NULL; data->restlen = 0; if (alg & SOAP_MEC_ENC) { if (!data->type) return soap_mec_check(soap, data, 0, "soap_mec_init() failed: cannot load cipher"); EVP_EncryptInit_ex(data->ctx, data->type, NULL, NULL, NULL); } if (alg & SOAP_MEC_OAEP) EVP_CIPHER_CTX_set_padding(data->ctx, RSA_PKCS1_OAEP_PADDING); else EVP_CIPHER_CTX_set_padding(data->ctx, RSA_PKCS1_PADDING); switch (alg & SOAP_MEC_MASK) { case SOAP_MEC_ENV_ENC_AES128_CBC: case SOAP_MEC_ENV_ENC_AES192_CBC: case SOAP_MEC_ENV_ENC_AES256_CBC: case SOAP_MEC_ENV_ENC_AES512_CBC: case SOAP_MEC_ENV_ENC_DES_CBC: ok = EVP_CIPHER_CTX_rand_key(data->ctx, data->ekey); /* generate ephemeral secret key */ #if (OPENSSL_VERSION_NUMBER >= 0x01000000L) *keylen = EVP_PKEY_encrypt_old(key, data->ekey, EVP_CIPHER_CTX_key_length(data->ctx), pkey); #else *keylen = EVP_PKEY_encrypt(key, data->ekey, EVP_CIPHER_CTX_key_length(data->ctx), pkey); #endif key = data->ekey; /* fall through to next arm */ case SOAP_MEC_ENC_DES_CBC: case SOAP_MEC_ENC_AES128_CBC: case SOAP_MEC_ENC_AES192_CBC: case SOAP_MEC_ENC_AES256_CBC: case SOAP_MEC_ENC_AES512_CBC: data->bufidx = 0; data->buflen = 1024; /* > iv in base64 must fit */ data->buf = (char*)SOAP_MALLOC(soap, data->buflen); data->key = key; break; case SOAP_MEC_ENV_DEC_AES128_CBC: case SOAP_MEC_ENV_DEC_AES192_CBC: case SOAP_MEC_ENV_DEC_AES256_CBC: case SOAP_MEC_ENV_DEC_AES512_CBC: case SOAP_MEC_ENV_DEC_DES_CBC: case SOAP_MEC_DEC_DES_CBC: case SOAP_MEC_DEC_AES128_CBC: case SOAP_MEC_DEC_AES192_CBC: case SOAP_MEC_DEC_AES256_CBC: case SOAP_MEC_DEC_AES512_CBC: data->pkey = pkey; data->key = key; data->keylen = *keylen; break; default: return soap_set_receiver_error(soap, "Unsupported encryption algorithm", NULL, SOAP_SSL_ERROR); } return soap_mec_check(soap, data, ok, "soap_mec_init() failed"); }
/** @fn int soap_smd_init(struct soap *soap, struct soap_smd_data *data, int alg, const void *key, int keylen) @brief Initiates a (signed) digest computation. @param soap context @param[in,out] data smdevp engine context @param[in] alg is algorithm to use @param[in] key is key to use or NULL for digests @param[in] keylen is length of HMAC key (when provided) @return SOAP_OK or SOAP_SSL_ERROR */ int soap_smd_init(struct soap *soap, struct soap_smd_data *data, int alg, const void *key, int keylen) { int ok = 1; const EVP_MD *type; soap_ssl_init(); /* the algorithm to use */ data->alg = alg; /* the key to use */ data->key = key; /* allocate and init the OpenSSL HMAC or EVP_MD context */ if ((alg & SOAP_SMD_ALGO) == SOAP_SMD_HMAC) { data->ctx = (void*)SOAP_MALLOC(soap, sizeof(HMAC_CTX)); if (!data->ctx) return soap_set_receiver_error(soap, "soap_smd_init() failed", "No context", SOAP_SSL_ERROR); HMAC_CTX_init((HMAC_CTX*)data->ctx); } else { data->ctx = (void*)SOAP_MALLOC(soap, sizeof(EVP_MD_CTX)); if (!data->ctx) return soap_set_receiver_error(soap, "soap_smd_init() failed", "No context", SOAP_SSL_ERROR); EVP_MD_CTX_init((EVP_MD_CTX*)data->ctx); } DBGLOG(TEST, SOAP_MESSAGE(fdebug, "-- SMD Init alg=%x (%p) --\n", alg, data->ctx)); /* init the digest or signature computations */ switch (alg & SOAP_SMD_HASH) { case SOAP_SMD_MD5: type = EVP_md5(); break; case SOAP_SMD_SHA1: type = EVP_sha1(); break; #if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) case SOAP_SMD_SHA224: type = EVP_sha224(); break; case SOAP_SMD_SHA256: type = EVP_sha256(); break; case SOAP_SMD_SHA384: type = EVP_sha384(); break; case SOAP_SMD_SHA512: type = EVP_sha512(); break; #endif default: return soap_smd_check(soap, data, 0, "soap_smd_init() failed: cannot load digest"); } switch (alg & SOAP_SMD_ALGO) { case SOAP_SMD_HMAC: HMAC_Init((HMAC_CTX*)data->ctx, key, keylen, type); break; case SOAP_SMD_DGST: EVP_DigestInit((EVP_MD_CTX*)data->ctx, type); break; case SOAP_SMD_SIGN: ok = EVP_SignInit((EVP_MD_CTX*)data->ctx, type); break; case SOAP_SMD_VRFY: ok = EVP_VerifyInit((EVP_MD_CTX*)data->ctx, type); break; default: return soap_set_receiver_error(soap, "Unsupported digest algorithm", NULL, SOAP_SSL_ERROR); } /* check and return */ return soap_smd_check(soap, data, ok, "soap_smd_init() failed"); }