static void dcrypt_gnutls_ctx_sym_set_key_iv_random(struct dcrypt_context_symmetric *ctx) { if(ctx->key.data != NULL) p_free(ctx->pool, ctx->key.data); if(ctx->iv.data != NULL) p_free(ctx->pool, ctx->iv.data); ctx->key.data = p_malloc(ctx->pool, gnutls_cipher_get_key_size(ctx->cipher)); random_fill(ctx->key.data, gnutls_cipher_get_key_size(ctx->cipher)); ctx->key.size = gnutls_cipher_get_key_size(ctx->cipher); ctx->iv.data = p_malloc(ctx->pool, gnutls_cipher_get_iv_size(ctx->cipher)); random_fill(ctx->iv.data, gnutls_cipher_get_iv_size(ctx->cipher)); ctx->iv.size = gnutls_cipher_get_iv_size(ctx->cipher); }
static void dcrypt_gnutls_ctx_sym_set_iv(struct dcrypt_context_symmetric *ctx, const unsigned char *iv, size_t iv_len) { if(ctx->iv.data != NULL) p_free(ctx->pool, ctx->iv.data); ctx->iv.size = I_MIN(iv_len,(size_t)gnutls_cipher_get_iv_size(ctx->cipher)); ctx->iv.data = p_malloc(ctx->pool, ctx->iv.size); memcpy(ctx->iv.data, iv, ctx->iv.size); }
static void cipher_bench(int algo, int size, int aead) { int ret; gnutls_cipher_hd_t ctx; void *_key, *_iv; gnutls_datum_t key, iv; int ivsize = gnutls_cipher_get_iv_size(algo); int keysize = gnutls_cipher_get_key_size(algo); int step = size * 1024; struct benchmark_st st; _key = malloc(keysize); if (_key == NULL) return; memset(_key, 0xf0, keysize); _iv = malloc(ivsize); if (_iv == NULL) return; memset(_iv, 0xf0, ivsize); iv.data = _iv; if (aead) iv.size = 12; else iv.size = ivsize; key.data = _key; key.size = keysize; printf("%16s ", gnutls_cipher_get_name(algo)); fflush(stdout); start_benchmark(&st); ret = gnutls_cipher_init(&ctx, algo, &key, &iv); if (ret < 0) { fprintf(stderr, "error: %s\n", gnutls_strerror(ret)); goto leave; } if (aead) gnutls_cipher_add_auth(ctx, data, 1024); do { gnutls_cipher_encrypt2(ctx, data, step, data, step + 64); st.size += step; } while (benchmark_must_finish == 0); gnutls_cipher_deinit(ctx); stop_benchmark(&st, NULL, 1); leave: free(_key); free(_iv); }
static void cipher_mac_bench(int algo, int mac_algo, int size) { int ret; gnutls_cipher_hd_t ctx; gnutls_hmac_hd_t mac_ctx; void *_key, *_iv; gnutls_datum_t key, iv; int ivsize = gnutls_cipher_get_iv_size(algo); int keysize = gnutls_cipher_get_key_size(algo); int step = size * 1024; struct benchmark_st st; void *output, *input; unsigned char c, *i; _key = malloc(keysize); if (_key == NULL) return; memset(_key, 0xf0, keysize); _iv = malloc(ivsize); if (_iv == NULL) { free(_key); return; } memset(_iv, 0xf0, ivsize); iv.data = _iv; iv.size = ivsize; key.data = _key; key.size = keysize; assert(gnutls_rnd(GNUTLS_RND_NONCE, &c, 1) >= 0); printf("%19s-%s ", gnutls_cipher_get_name(algo), gnutls_mac_get_name(mac_algo)); fflush(stdout); ALLOCM(input, MAX_MEM); ALLOC(output); i = input; start_benchmark(&st); ret = gnutls_hmac_init(&mac_ctx, mac_algo, key.data, key.size); if (ret < 0) { fprintf(stderr, "error: %s\n", gnutls_strerror(ret)); goto leave; } ret = gnutls_cipher_init(&ctx, algo, &key, &iv); if (ret < 0) { fprintf(stderr, "error: %s\n", gnutls_strerror(ret)); goto leave; } do { gnutls_hmac(mac_ctx, i, step); gnutls_cipher_encrypt2(ctx, i, step, output, step + 64); st.size += step; INC(input, i, step); } while (benchmark_must_finish == 0); gnutls_cipher_deinit(ctx); gnutls_hmac_deinit(mac_ctx, NULL); stop_benchmark(&st, NULL, 1); leave: FREE(input); FREE(output); free(_key); free(_iv); }
static void cipher_bench(int algo, int size, int aead) { int ret; gnutls_cipher_hd_t ctx; void *_key, *_iv; gnutls_aead_cipher_hd_t actx; gnutls_datum_t key, iv; int ivsize = gnutls_cipher_get_iv_size(algo); int keysize = gnutls_cipher_get_key_size(algo); int step = size * 1024; void *input, *output; struct benchmark_st st; unsigned char c, *i; _key = malloc(keysize); if (_key == NULL) return; memset(_key, 0xf0, keysize); _iv = malloc(ivsize); if (_iv == NULL) { free(_key); return; } memset(_iv, 0xf0, ivsize); iv.data = _iv; iv.size = ivsize; key.data = _key; key.size = keysize; printf("%24s ", gnutls_cipher_get_name(algo)); fflush(stdout); assert(gnutls_rnd(GNUTLS_RND_NONCE, &c, 1) >= 0); ALLOCM(input, MAX_MEM); ALLOCM(output, step+64); i = input; start_benchmark(&st); if (algo == GNUTLS_CIPHER_NULL) { do { force_memcpy(output, i, step); st.size += step; INC(input, i, step); } while (benchmark_must_finish == 0); } else if (aead != 0) { unsigned tag_size = gnutls_cipher_get_tag_size(algo); size_t out_size; ret = gnutls_aead_cipher_init(&actx, algo, &key); if (ret < 0) { fprintf(stderr, "error: %s\n", gnutls_strerror(ret)); goto leave; } do { out_size = step+64; assert(gnutls_aead_cipher_encrypt(actx, iv.data, iv.size, NULL, 0, tag_size, i, step, output, &out_size) >= 0); st.size += step; INC(input, i, step); } while (benchmark_must_finish == 0); gnutls_aead_cipher_deinit(actx); } else { ret = gnutls_cipher_init(&ctx, algo, &key, &iv); if (ret < 0) { fprintf(stderr, "error: %s\n", gnutls_strerror(ret)); goto leave; } do { gnutls_cipher_encrypt2(ctx, i, step, output, step + 64); st.size += step; INC(input, i, step); } while (benchmark_must_finish == 0); gnutls_cipher_deinit(ctx); } stop_benchmark(&st, NULL, 1); FREE(input); FREE(output); leave: free(_key); free(_iv); }
/* * Helper function converts a data blob to a gnutls_datum_t. * Note that this does not copy the data. * So the returned value should be treated as read only. * And that changes to the length of the underlying DATA_BLOB * will not be reflected in the returned object. * */ static const gnutls_datum_t convert_from_data_blob(DATA_BLOB blob) { const gnutls_datum_t datum = { .size = blob.length, .data = blob.data, }; return datum; } /* * @brief Get the gnutls algorithm needed to decrypt the EncryptedSecret * * @param ldb ldb context, to allow logging. * @param es the encrypted secret * * @return The gnutls algoritm number, or 0 if there is no match. * */ static int gnutls_get_algorithm(struct ldb_context *ldb, struct EncryptedSecret *es) { switch (es->header.algorithm) { case ENC_SECRET_AES_128_AEAD: return GNUTLS_CIPHER_AES_128_GCM; default: ldb_asprintf_errstring(ldb, "Unsupported encryption algorithm %d\n", es->header.algorithm); return 0; } } /* * * @param err Pointer to an error code, set to: * LDB_SUCESS If the value was successfully encrypted * LDB_ERR_OPERATIONS_ERROR If there was an error. * * @param ctx Talloc memory context the will own the memory allocated * @param ldb ldb context, to allow logging. * @param val The ldb value to encrypt, not altered or freed * @param data The context data for this module. * * @return The encrypted ldb_val, or data_blob_null if there was an error. */ static struct ldb_val gnutls_encrypt_aead(int *err, TALLOC_CTX *ctx, struct ldb_context *ldb, const struct ldb_val val, const struct es_data *data) { struct EncryptedSecret *es = NULL; struct ldb_val enc = data_blob_null; DATA_BLOB pt = data_blob_null; gnutls_aead_cipher_hd_t cipher_hnd; int rc; TALLOC_CTX *frame = talloc_stackframe(); es = makeEncryptedSecret(ldb, frame); if (es == NULL) { goto error_exit; } pt = makePlainText(frame, ldb, val); if (pt.length == 0) { goto error_exit; } /* * Set the encryption key and initialize the encryption handle. */ { const size_t key_size = gnutls_cipher_get_key_size( data->encryption_algorithm); gnutls_datum_t cipher_key; DATA_BLOB key_blob = get_key(data); if (key_blob.length != key_size) { ldb_asprintf_errstring(ldb, "Invalid EncryptedSecrets key " "size, expected %zu bytes and " "it is %zu bytes\n", key_size, key_blob.length); goto error_exit; } cipher_key = convert_from_data_blob(key_blob); rc = gnutls_aead_cipher_init(&cipher_hnd, data->encryption_algorithm, &cipher_key); if (rc !=0) { ldb_asprintf_errstring(ldb, "gnutls_aead_cipher_init failed " "%s - %s\n", gnutls_strerror_name(rc), gnutls_strerror(rc)); goto error_exit; } } /* * Set the initialisation vector */ { unsigned iv_size = gnutls_cipher_get_iv_size( data->encryption_algorithm); uint8_t *iv; iv = talloc_zero_size(frame, iv_size); if (iv == NULL) { ldb_set_errstring(ldb, "Out of memory allocating IV\n"); goto error_exit_handle; } rc = gnutls_rnd(GNUTLS_RND_NONCE, iv, iv_size); if (rc !=0) { ldb_asprintf_errstring(ldb, "gnutls_rnd failed %s - %s\n", gnutls_strerror_name(rc), gnutls_strerror(rc)); goto error_exit_handle; } es->iv.length = iv_size; es->iv.data = iv; } /* * Encrypt the value. */ { const unsigned block_size = gnutls_cipher_get_block_size( data->encryption_algorithm); const unsigned tag_size = gnutls_cipher_get_tag_size( data->encryption_algorithm); const size_t ed_size = round_to_block_size( block_size, sizeof(struct PlaintextSecret) + val.length); const size_t en_size = ed_size + tag_size; uint8_t *ct = talloc_zero_size(frame, en_size); size_t el = en_size; if (ct == NULL) { ldb_set_errstring(ldb, "Out of memory allocation cipher " "text\n"); goto error_exit_handle; } rc = gnutls_aead_cipher_encrypt( cipher_hnd, es->iv.data, es->iv.length, &es->header, sizeof(struct EncryptedSecretHeader), tag_size, pt.data, pt.length, ct, &el); if (rc !=0) { ldb_asprintf_errstring(ldb, "gnutls_aead_cipher_encrypt '" "failed %s - %s\n", gnutls_strerror_name(rc), gnutls_strerror(rc)); *err = LDB_ERR_OPERATIONS_ERROR; return data_blob_null; } es->encrypted.length = el; es->encrypted.data = ct; gnutls_aead_cipher_deinit(cipher_hnd); } rc = ndr_push_struct_blob(&enc, ctx, es, (ndr_push_flags_fn_t) ndr_push_EncryptedSecret); if (!NDR_ERR_CODE_IS_SUCCESS(rc)) { ldb_set_errstring(ldb, "Unable to ndr push EncryptedSecret\n"); goto error_exit; } TALLOC_FREE(frame); return enc; error_exit_handle: gnutls_aead_cipher_deinit(cipher_hnd); error_exit: *err = LDB_ERR_OPERATIONS_ERROR; TALLOC_FREE(frame); return data_blob_null; } /* * @brief Decrypt data encrypted using an aead algorithm. * * Decrypt the data in ed and insert it into ev. The data was encrypted * with one of the gnutls aead compatable algorithms. * * @param err Pointer to an error code, set to: * LDB_SUCESS If the value was successfully decrypted * LDB_ERR_OPERATIONS_ERROR If there was an error. * * @param ctx The talloc context that will own the PlaintextSecret * @param ldb ldb context, to allow logging. * @param ev The value to be updated with the decrypted data. * @param ed The data to decrypt. * @param data The context data for this module. * * @return ev is updated with the unencrypted data. */ static void gnutls_decrypt_aead(int *err, TALLOC_CTX *ctx, struct ldb_context *ldb, struct EncryptedSecret *es, struct PlaintextSecret *ps, const struct es_data *data) { gnutls_aead_cipher_hd_t cipher_hnd; DATA_BLOB pt = data_blob_null; const unsigned tag_size = gnutls_cipher_get_tag_size(es->header.algorithm); int rc; /* * Get the encryption key and initialise the encryption handle */ { gnutls_datum_t cipher_key; DATA_BLOB key_blob; const int algorithm = gnutls_get_algorithm(ldb, es); const size_t key_size = gnutls_cipher_get_key_size(algorithm); key_blob = get_key(data); if (algorithm == 0) { goto error_exit; } if (key_blob.length != key_size) { ldb_asprintf_errstring(ldb, "Invalid EncryptedSecrets key " "size, expected %zu bytes and " "it is %zu bytes\n", key_size, key_blob.length); goto error_exit; } cipher_key = convert_from_data_blob(key_blob); rc = gnutls_aead_cipher_init( &cipher_hnd, algorithm, &cipher_key); if (rc != 0) { ldb_asprintf_errstring(ldb, "gnutls_aead_cipher_init failed " "%s - %s\n", gnutls_strerror_name(rc), gnutls_strerror(rc)); goto error_exit; } } /* * Decrypt and validate the encrypted value */ pt.length = es->encrypted.length; pt.data = talloc_zero_size(ctx, es->encrypted.length); if (pt.data == NULL) { ldb_set_errstring(ldb, "Out of memory allocating plain text\n"); goto error_exit_handle; } rc = gnutls_aead_cipher_decrypt(cipher_hnd, es->iv.data, es->iv.length, &es->header, sizeof(struct EncryptedSecretHeader), tag_size, es->encrypted.data, es->encrypted.length, pt.data, &pt.length); if (rc != 0) { /* * Typically this will indicate that the data has been * corrupted i.e. the tag comparison has failed. * At the moment gnutls does not provide a separate * error code to indicate this */ ldb_asprintf_errstring(ldb, "gnutls_aead_cipher_decrypt failed " "%s - %s. Data possibly corrupted or " "altered\n", gnutls_strerror_name(rc), gnutls_strerror(rc)); goto error_exit_handle; } gnutls_aead_cipher_deinit(cipher_hnd); rc = ndr_pull_struct_blob(&pt, ctx, ps, (ndr_pull_flags_fn_t) ndr_pull_PlaintextSecret); if(!NDR_ERR_CODE_IS_SUCCESS(rc)) { ldb_asprintf_errstring(ldb, "Error(%d) unpacking decrypted data, " "data possibly corrupted or altered\n", rc); goto error_exit; } return; error_exit_handle: gnutls_aead_cipher_deinit(cipher_hnd); error_exit: *err = LDB_ERR_OPERATIONS_ERROR; return; }
/** * gnutls_x509_privkey_import_openssl: * @key: The structure to store the parsed key * @data: The DER or PEM encoded key. * @password: the password to decrypt the key (if it is encrypted). * * This function will convert the given PEM encrypted to * the native gnutls_x509_privkey_t format. The * output will be stored in @key. * * The @password should be in ASCII. If the password is not provided * or wrong then %GNUTLS_E_DECRYPTION_FAILED will be returned. * * If the Certificate is PEM encoded it should have a header of * "PRIVATE KEY" and the "DEK-Info" header. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. **/ int gnutls_x509_privkey_import_openssl(gnutls_x509_privkey_t key, const gnutls_datum_t * data, const char *password) { gnutls_cipher_hd_t handle; gnutls_cipher_algorithm_t cipher = GNUTLS_CIPHER_UNKNOWN; gnutls_datum_t b64_data; gnutls_datum_t salt, enc_key; unsigned char *key_data; size_t key_data_size; const char *pem_header = (void *) data->data; const char *pem_header_start = (void *) data->data; ssize_t pem_header_size; int ret; unsigned int i, iv_size, l; pem_header_size = data->size; pem_header = memmem(pem_header, pem_header_size, "PRIVATE KEY---", 14); if (pem_header == NULL) { gnutls_assert(); return GNUTLS_E_PARSING_ERROR; } pem_header_size -= (ptrdiff_t) (pem_header - pem_header_start); pem_header = memmem(pem_header, pem_header_size, "DEK-Info: ", 10); if (pem_header == NULL) { gnutls_assert(); return GNUTLS_E_PARSING_ERROR; } pem_header_size = data->size - (ptrdiff_t) (pem_header - pem_header_start) - 10; pem_header += 10; for (i = 0; i < sizeof(pem_ciphers) / sizeof(pem_ciphers[0]); i++) { l = strlen(pem_ciphers[i].name); if (!strncmp(pem_header, pem_ciphers[i].name, l) && pem_header[l] == ',') { pem_header += l + 1; cipher = pem_ciphers[i].cipher; break; } } if (cipher == GNUTLS_CIPHER_UNKNOWN) { _gnutls_debug_log ("Unsupported PEM encryption type: %.10s\n", pem_header); gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } iv_size = gnutls_cipher_get_iv_size(cipher); salt.size = iv_size; salt.data = gnutls_malloc(salt.size); if (!salt.data) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); for (i = 0; i < salt.size * 2; i++) { unsigned char x; const char *c = &pem_header[i]; if (*c >= '0' && *c <= '9') x = (*c) - '0'; else if (*c >= 'A' && *c <= 'F') x = (*c) - 'A' + 10; else { gnutls_assert(); /* Invalid salt in encrypted PEM file */ ret = GNUTLS_E_INVALID_REQUEST; goto out_salt; } if (i & 1) salt.data[i / 2] |= x; else salt.data[i / 2] = x << 4; } pem_header += salt.size * 2; if (*pem_header != '\r' && *pem_header != '\n') { gnutls_assert(); ret = GNUTLS_E_INVALID_REQUEST; goto out_salt; } while (*pem_header == '\n' || *pem_header == '\r') pem_header++; ret = _gnutls_base64_decode((const void *) pem_header, pem_header_size, &b64_data); if (ret < 0) { gnutls_assert(); goto out_salt; } if (b64_data.size < 16) { /* Just to be sure our parsing is OK */ gnutls_assert(); ret = GNUTLS_E_PARSING_ERROR; goto out_b64; } ret = GNUTLS_E_MEMORY_ERROR; enc_key.size = gnutls_cipher_get_key_size(cipher); enc_key.data = gnutls_malloc(enc_key.size); if (!enc_key.data) { ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); goto out_b64; } key_data_size = b64_data.size; key_data = gnutls_malloc(key_data_size); if (!key_data) { ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); goto out_enc_key; } while (1) { memcpy(key_data, b64_data.data, key_data_size); ret = openssl_hash_password(password, &enc_key, &salt); if (ret < 0) { gnutls_assert(); goto out; } ret = gnutls_cipher_init(&handle, cipher, &enc_key, &salt); if (ret < 0) { gnutls_assert(); gnutls_cipher_deinit(handle); goto out; } ret = gnutls_cipher_decrypt(handle, key_data, key_data_size); gnutls_cipher_deinit(handle); if (ret < 0) { gnutls_assert(); goto out; } /* We have to strip any padding to accept it. So a bit more ASN.1 parsing for us. */ if (key_data[0] == 0x30) { gnutls_datum_t key_datum; unsigned int blocksize = gnutls_cipher_get_block_size(cipher); unsigned int keylen = key_data[1]; unsigned int ofs = 2; if (keylen & 0x80) { int lenlen = keylen & 0x7f; keylen = 0; if (lenlen > 3) { gnutls_assert(); goto fail; } while (lenlen) { keylen <<= 8; keylen |= key_data[ofs++]; lenlen--; } } keylen += ofs; /* If there appears to be more padding than required, fail */ if (key_data_size - keylen > blocksize) { gnutls_assert(); goto fail; } /* If the padding bytes aren't all equal to the amount of padding, fail */ ofs = keylen; while (ofs < key_data_size) { if (key_data[ofs] != key_data_size - keylen) { gnutls_assert(); goto fail; } ofs++; } key_datum.data = key_data; key_datum.size = keylen; ret = gnutls_x509_privkey_import(key, &key_datum, GNUTLS_X509_FMT_DER); if (ret == 0) goto out; } fail: ret = GNUTLS_E_DECRYPTION_FAILED; goto out; } out: zeroize_key(key_data, key_data_size); gnutls_free(key_data); out_enc_key: _gnutls_free_key_datum(&enc_key); out_b64: gnutls_free(b64_data.data); out_salt: gnutls_free(salt.data); return ret; }
static int dcrypt_gnutls_ctx_sym_get_iv_length(struct dcrypt_context_symmetric *ctx) { return gnutls_cipher_get_iv_size(ctx->cipher); }
static int cipher_test(const char *ocipher, gnutls_cipher_algorithm_t gcipher, unsigned tag_size) { int ret; gnutls_aead_cipher_hd_t hd; gnutls_datum_t dkey, dnonce; unsigned char key[32]; unsigned char nonce[32]; size_t enc_data_size, dec_data_size; int dec_data_size2; EVP_CIPHER_CTX *ctx; const EVP_CIPHER *evp_cipher; unsigned char tag[64]; assert(gnutls_rnd(GNUTLS_RND_NONCE, orig_plain_data, sizeof(orig_plain_data)) >= 0); assert(gnutls_rnd(GNUTLS_RND_NONCE, buffer_auth, sizeof(buffer_auth)) >= 0); assert(gnutls_rnd(GNUTLS_RND_NONCE, key, sizeof(key)) >= 0); assert(gnutls_rnd(GNUTLS_RND_NONCE, nonce, sizeof(nonce)) >= 0); dkey.data = (void*)key; dkey.size = gnutls_cipher_get_key_size(gcipher); assert(gnutls_aead_cipher_init(&hd, gcipher, &dkey) >= 0); dnonce.data = (void*)nonce; dnonce.size = gnutls_cipher_get_iv_size(gcipher); enc_data_size = sizeof(enc_data); assert(gnutls_aead_cipher_encrypt(hd, dnonce.data, dnonce.size, buffer_auth, sizeof(buffer_auth), tag_size, orig_plain_data, sizeof(orig_plain_data), enc_data, &enc_data_size) >= 0); if (debug) success("encrypted %d bytes, to %d\n", (int)sizeof(orig_plain_data), (int)enc_data_size); dec_data_size = sizeof(dec_data); ret = gnutls_aead_cipher_decrypt(hd, dnonce.data, dnonce.size, buffer_auth, sizeof(buffer_auth), tag_size, enc_data, enc_data_size, dec_data, &dec_data_size); if (ret < 0) { fail("error in gnutls_aead_cipher_decrypt for %s: %s\n", ocipher, gnutls_strerror(ret)); } if (dec_data_size != sizeof(orig_plain_data) || memcmp(dec_data, orig_plain_data, sizeof(orig_plain_data)) != 0) { fail("gnutls encrypt-decrypt failed (got: %d, expected: %d)\n", (int)dec_data_size, (int)sizeof(orig_plain_data)); } gnutls_aead_cipher_deinit(hd); /* decrypt with openssl */ evp_cipher = EVP_get_cipherbyname(ocipher); if (!evp_cipher) fail("EVP_get_cipherbyname failed for %s\n", ocipher); ctx = EVP_CIPHER_CTX_new(); assert(EVP_CipherInit_ex(ctx, evp_cipher, NULL, key, nonce, 0) > 0); EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, tag_size, enc_data+enc_data_size-tag_size); dec_data_size2 = sizeof(dec_data); assert(EVP_CipherUpdate(ctx, NULL, &dec_data_size2, buffer_auth, sizeof(buffer_auth)) > 0); dec_data_size2 = sizeof(dec_data); assert(EVP_CipherUpdate(ctx, dec_data, &dec_data_size2, enc_data, enc_data_size-tag_size) > 0); dec_data_size = dec_data_size2; dec_data_size2 = tag_size; assert(EVP_CipherFinal_ex(ctx, tag, &dec_data_size2) > 0); if (dec_data_size != sizeof(orig_plain_data) || memcmp(dec_data, orig_plain_data, sizeof(orig_plain_data)) != 0) { fail("openssl decrypt failed for %s\n", ocipher); } EVP_CIPHER_CTX_free(ctx); return 0; }