/* * 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; }
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); }
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; }