aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/tsi/alts/zero_copy_frame_protector
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/tsi/alts/zero_copy_frame_protector')
-rw-r--r--src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc180
-rw-r--r--src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h52
-rw-r--r--src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc144
-rw-r--r--src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h49
-rw-r--r--src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h91
-rw-r--r--src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc173
-rw-r--r--src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h100
-rw-r--r--src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc476
-rw-r--r--src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h199
-rw-r--r--src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc295
-rw-r--r--src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h52
11 files changed, 1811 insertions, 0 deletions
diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc
new file mode 100644
index 0000000000..7ba03eb7f0
--- /dev/null
+++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc
@@ -0,0 +1,180 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h"
+
+/* Main struct for alts_grpc_integrity_only_record_protocol. */
+typedef struct alts_grpc_integrity_only_record_protocol {
+ alts_grpc_record_protocol base;
+ grpc_slice_buffer data_sb;
+ unsigned char* tag_buf;
+} alts_grpc_integrity_only_record_protocol;
+
+/* --- alts_grpc_record_protocol methods implementation. --- */
+
+static tsi_result alts_grpc_integrity_only_protect(
+ alts_grpc_record_protocol* rp, grpc_slice_buffer* unprotected_slices,
+ grpc_slice_buffer* protected_slices) {
+ /* Input sanity check. */
+ if (rp == nullptr || unprotected_slices == nullptr ||
+ protected_slices == nullptr) {
+ gpr_log(GPR_ERROR,
+ "Invalid nullptr arguments to alts_grpc_record_protocol protect.");
+ return TSI_INVALID_ARGUMENT;
+ }
+ /* Allocates memory for header and tag slices. */
+ grpc_slice header_slice = GRPC_SLICE_MALLOC(rp->header_length);
+ grpc_slice tag_slice = GRPC_SLICE_MALLOC(rp->tag_length);
+ /* Calls alts_iovec_record_protocol protect. */
+ char* error_details = nullptr;
+ iovec_t header_iovec = {GRPC_SLICE_START_PTR(header_slice),
+ GRPC_SLICE_LENGTH(header_slice)};
+ iovec_t tag_iovec = {GRPC_SLICE_START_PTR(tag_slice),
+ GRPC_SLICE_LENGTH(tag_slice)};
+ alts_grpc_record_protocol_convert_slice_buffer_to_iovec(rp,
+ unprotected_slices);
+ grpc_status_code status = alts_iovec_record_protocol_integrity_only_protect(
+ rp->iovec_rp, rp->iovec_buf, unprotected_slices->count, header_iovec,
+ tag_iovec, &error_details);
+ if (status != GRPC_STATUS_OK) {
+ gpr_log(GPR_ERROR, "Failed to protect, %s", error_details);
+ gpr_free(error_details);
+ return TSI_INTERNAL_ERROR;
+ }
+ /* Appends result to protected_slices. */
+ grpc_slice_buffer_add(protected_slices, header_slice);
+ grpc_slice_buffer_move_into(unprotected_slices, protected_slices);
+ grpc_slice_buffer_add(protected_slices, tag_slice);
+ return TSI_OK;
+}
+
+static tsi_result alts_grpc_integrity_only_unprotect(
+ alts_grpc_record_protocol* rp, grpc_slice_buffer* protected_slices,
+ grpc_slice_buffer* unprotected_slices) {
+ /* Input sanity check. */
+ if (rp == nullptr || protected_slices == nullptr ||
+ unprotected_slices == nullptr) {
+ gpr_log(
+ GPR_ERROR,
+ "Invalid nullptr arguments to alts_grpc_record_protocol unprotect.");
+ return TSI_INVALID_ARGUMENT;
+ }
+ if (protected_slices->length < rp->header_length + rp->tag_length) {
+ gpr_log(GPR_ERROR, "Protected slices do not have sufficient data.");
+ return TSI_INVALID_ARGUMENT;
+ }
+ /* In this method, rp points to alts_grpc_record_protocol struct
+ * and integrity_only_record_protocol points to
+ * alts_grpc_integrity_only_record_protocol struct. */
+ alts_grpc_integrity_only_record_protocol* integrity_only_record_protocol =
+ reinterpret_cast<alts_grpc_integrity_only_record_protocol*>(rp);
+ /* Strips frame header from protected slices. */
+ grpc_slice_buffer_reset_and_unref_internal(&rp->header_sb);
+ grpc_slice_buffer_move_first(protected_slices, rp->header_length,
+ &rp->header_sb);
+ GPR_ASSERT(rp->header_sb.length == rp->header_length);
+ iovec_t header_iovec = alts_grpc_record_protocol_get_header_iovec(rp);
+ /* Moves protected slices data to data_sb and leaves the remaining tag. */
+ grpc_slice_buffer_reset_and_unref_internal(
+ &integrity_only_record_protocol->data_sb);
+ grpc_slice_buffer_move_first(protected_slices,
+ protected_slices->length - rp->tag_length,
+ &integrity_only_record_protocol->data_sb);
+ GPR_ASSERT(protected_slices->length == rp->tag_length);
+ iovec_t tag_iovec = {nullptr, rp->tag_length};
+ if (protected_slices->count == 1) {
+ tag_iovec.iov_base = GRPC_SLICE_START_PTR(protected_slices->slices[0]);
+ } else {
+ /* Frame tag is in multiple slices, copies the tag bytes from slice
+ * buffer to a single flat buffer. */
+ alts_grpc_record_protocol_copy_slice_buffer(
+ protected_slices, integrity_only_record_protocol->tag_buf);
+ tag_iovec.iov_base = integrity_only_record_protocol->tag_buf;
+ }
+ /* Calls alts_iovec_record_protocol unprotect. */
+ char* error_details = nullptr;
+ alts_grpc_record_protocol_convert_slice_buffer_to_iovec(
+ rp, &integrity_only_record_protocol->data_sb);
+ grpc_status_code status = alts_iovec_record_protocol_integrity_only_unprotect(
+ rp->iovec_rp, rp->iovec_buf,
+ integrity_only_record_protocol->data_sb.count, header_iovec, tag_iovec,
+ &error_details);
+ if (status != GRPC_STATUS_OK) {
+ gpr_log(GPR_ERROR, "Failed to unprotect, %s", error_details);
+ gpr_free(error_details);
+ return TSI_INTERNAL_ERROR;
+ }
+ grpc_slice_buffer_reset_and_unref_internal(&rp->header_sb);
+ grpc_slice_buffer_reset_and_unref_internal(protected_slices);
+ grpc_slice_buffer_move_into(&integrity_only_record_protocol->data_sb,
+ unprotected_slices);
+ return TSI_OK;
+}
+
+static void alts_grpc_integrity_only_destruct(alts_grpc_record_protocol* rp) {
+ if (rp == nullptr) {
+ return;
+ }
+ alts_grpc_integrity_only_record_protocol* integrity_only_rp =
+ reinterpret_cast<alts_grpc_integrity_only_record_protocol*>(rp);
+ grpc_slice_buffer_destroy_internal(&integrity_only_rp->data_sb);
+ gpr_free(integrity_only_rp->tag_buf);
+}
+
+static const alts_grpc_record_protocol_vtable
+ alts_grpc_integrity_only_record_protocol_vtable = {
+ alts_grpc_integrity_only_protect, alts_grpc_integrity_only_unprotect,
+ alts_grpc_integrity_only_destruct};
+
+tsi_result alts_grpc_integrity_only_record_protocol_create(
+ gsec_aead_crypter* crypter, size_t overflow_size, bool is_client,
+ bool is_protect, alts_grpc_record_protocol** rp) {
+ if (crypter == nullptr || rp == nullptr) {
+ gpr_log(GPR_ERROR,
+ "Invalid nullptr arguments to alts_grpc_record_protocol create.");
+ return TSI_INVALID_ARGUMENT;
+ }
+ alts_grpc_integrity_only_record_protocol* impl =
+ static_cast<alts_grpc_integrity_only_record_protocol*>(
+ gpr_zalloc(sizeof(alts_grpc_integrity_only_record_protocol)));
+ /* Calls alts_grpc_record_protocol init. */
+ tsi_result result = alts_grpc_record_protocol_init(
+ &impl->base, crypter, overflow_size, is_client,
+ /*is_integrity_only=*/true, is_protect);
+ if (result != TSI_OK) {
+ gpr_free(impl);
+ return result;
+ }
+ /* Initializes slice buffer for data_sb. */
+ grpc_slice_buffer_init(&impl->data_sb);
+ /* Allocates tag buffer. */
+ impl->tag_buf =
+ static_cast<unsigned char*>(gpr_malloc(impl->base.tag_length));
+ impl->base.vtable = &alts_grpc_integrity_only_record_protocol_vtable;
+ *rp = &impl->base;
+ return TSI_OK;
+}
diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h
new file mode 100644
index 0000000000..8d68b27e07
--- /dev/null
+++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_INTEGRITY_ONLY_RECORD_PROTOCOL_H
+#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_INTEGRITY_ONLY_RECORD_PROTOCOL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include "src/core/tsi/alts/crypt/gsec.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h"
+
+/**
+ * This method creates an integrity-only alts_grpc_record_protocol instance,
+ * given a gsec_aead_crypter instance and a flag indicating if the created
+ * instance will be used at the client or server side. The ownership of
+ * gsec_aead_crypter instance is transferred to this new object.
+ *
+ * - crypter: a gsec_aead_crypter instance used to perform AEAD decryption.
+ * - overflow_size: overflow size of counter in bytes.
+ * - is_client: a flag indicating if the alts_grpc_record_protocol instance will
+ * be used at the client or server side.
+ * - is_protect: a flag indicating if the alts_grpc_record_protocol instance
+ * will be used for protect or unprotect.
+ * - rp: an alts_grpc_record_protocol instance to be returned from
+ * the method.
+ *
+ * This method returns TSI_OK in case of success or a specific error code in
+ * case of failure.
+ */
+tsi_result alts_grpc_integrity_only_record_protocol_create(
+ gsec_aead_crypter* crypter, size_t overflow_size, bool is_client,
+ bool is_protect, alts_grpc_record_protocol** rp);
+
+#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_INTEGRITY_ONLY_RECORD_PROTOCOL_H \
+ */
diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc
new file mode 100644
index 0000000000..d4fd88d1e2
--- /dev/null
+++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc
@@ -0,0 +1,144 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h"
+
+/* Privacy-integrity alts_grpc_record_protocol object uses the same struct
+ * defined in alts_grpc_record_protocol_common.h. */
+
+/* --- alts_grpc_record_protocol methods implementation. --- */
+
+static tsi_result alts_grpc_privacy_integrity_protect(
+ alts_grpc_record_protocol* rp, grpc_slice_buffer* unprotected_slices,
+ grpc_slice_buffer* protected_slices) {
+ /* Input sanity check. */
+ if (rp == nullptr || unprotected_slices == nullptr ||
+ protected_slices == nullptr) {
+ gpr_log(GPR_ERROR,
+ "Invalid nullptr arguments to alts_grpc_record_protocol protect.");
+ return TSI_INVALID_ARGUMENT;
+ }
+ /* Allocates memory for output frame. In privacy-integrity protect, the
+ * protected frame is stored in a newly allocated buffer. */
+ size_t protected_frame_size =
+ unprotected_slices->length + rp->header_length +
+ alts_iovec_record_protocol_get_tag_length(rp->iovec_rp);
+ grpc_slice protected_slice = GRPC_SLICE_MALLOC(protected_frame_size);
+ iovec_t protected_iovec = {GRPC_SLICE_START_PTR(protected_slice),
+ GRPC_SLICE_LENGTH(protected_slice)};
+ /* Calls alts_iovec_record_protocol protect. */
+ char* error_details = nullptr;
+ alts_grpc_record_protocol_convert_slice_buffer_to_iovec(rp,
+ unprotected_slices);
+ grpc_status_code status =
+ alts_iovec_record_protocol_privacy_integrity_protect(
+ rp->iovec_rp, rp->iovec_buf, unprotected_slices->count,
+ protected_iovec, &error_details);
+ if (status != GRPC_STATUS_OK) {
+ gpr_log(GPR_ERROR, "Failed to protect, %s", error_details);
+ gpr_free(error_details);
+ grpc_slice_unref(protected_slice);
+ return TSI_INTERNAL_ERROR;
+ }
+ grpc_slice_buffer_add(protected_slices, protected_slice);
+ grpc_slice_buffer_reset_and_unref_internal(unprotected_slices);
+ return TSI_OK;
+}
+
+static tsi_result alts_grpc_privacy_integrity_unprotect(
+ alts_grpc_record_protocol* rp, grpc_slice_buffer* protected_slices,
+ grpc_slice_buffer* unprotected_slices) {
+ /* Input sanity check. */
+ if (rp == nullptr || protected_slices == nullptr ||
+ unprotected_slices == nullptr) {
+ gpr_log(
+ GPR_ERROR,
+ "Invalid nullptr arguments to alts_grpc_record_protocol unprotect.");
+ return TSI_INVALID_ARGUMENT;
+ }
+ /* Allocates memory for output frame. In privacy-integrity unprotect, the
+ * unprotected data are stored in a newly allocated buffer. */
+ if (protected_slices->length < rp->header_length + rp->tag_length) {
+ gpr_log(GPR_ERROR, "Protected slices do not have sufficient data.");
+ return TSI_INVALID_ARGUMENT;
+ }
+ size_t unprotected_frame_size =
+ protected_slices->length - rp->header_length - rp->tag_length;
+ grpc_slice unprotected_slice = GRPC_SLICE_MALLOC(unprotected_frame_size);
+ iovec_t unprotected_iovec = {GRPC_SLICE_START_PTR(unprotected_slice),
+ GRPC_SLICE_LENGTH(unprotected_slice)};
+ /* Strips frame header from protected slices. */
+ grpc_slice_buffer_reset_and_unref_internal(&rp->header_sb);
+ grpc_slice_buffer_move_first(protected_slices, rp->header_length,
+ &rp->header_sb);
+ iovec_t header_iovec = alts_grpc_record_protocol_get_header_iovec(rp);
+ /* Calls alts_iovec_record_protocol unprotect. */
+ char* error_details = nullptr;
+ alts_grpc_record_protocol_convert_slice_buffer_to_iovec(rp, protected_slices);
+ grpc_status_code status =
+ alts_iovec_record_protocol_privacy_integrity_unprotect(
+ rp->iovec_rp, header_iovec, rp->iovec_buf, protected_slices->count,
+ unprotected_iovec, &error_details);
+ if (status != GRPC_STATUS_OK) {
+ gpr_log(GPR_ERROR, "Failed to unprotect, %s", error_details);
+ gpr_free(error_details);
+ grpc_slice_unref(unprotected_slice);
+ return TSI_INTERNAL_ERROR;
+ }
+ grpc_slice_buffer_reset_and_unref_internal(&rp->header_sb);
+ grpc_slice_buffer_reset_and_unref_internal(protected_slices);
+ grpc_slice_buffer_add(unprotected_slices, unprotected_slice);
+ return TSI_OK;
+}
+
+static const alts_grpc_record_protocol_vtable
+ alts_grpc_privacy_integrity_record_protocol_vtable = {
+ alts_grpc_privacy_integrity_protect,
+ alts_grpc_privacy_integrity_unprotect, nullptr};
+
+tsi_result alts_grpc_privacy_integrity_record_protocol_create(
+ gsec_aead_crypter* crypter, size_t overflow_size, bool is_client,
+ bool is_protect, alts_grpc_record_protocol** rp) {
+ if (crypter == nullptr || rp == nullptr) {
+ gpr_log(GPR_ERROR,
+ "Invalid nullptr arguments to alts_grpc_record_protocol create.");
+ return TSI_INVALID_ARGUMENT;
+ }
+ auto* impl = static_cast<alts_grpc_record_protocol*>(
+ gpr_zalloc(sizeof(alts_grpc_record_protocol)));
+ /* Calls alts_grpc_record_protocol init. */
+ tsi_result result =
+ alts_grpc_record_protocol_init(impl, crypter, overflow_size, is_client,
+ /*is_integrity_only=*/false, is_protect);
+ if (result != TSI_OK) {
+ gpr_free(impl);
+ return result;
+ }
+ impl->vtable = &alts_grpc_privacy_integrity_record_protocol_vtable;
+ *rp = impl;
+ return TSI_OK;
+}
diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h
new file mode 100644
index 0000000000..1e34aef2d8
--- /dev/null
+++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h
@@ -0,0 +1,49 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_PRIVACY_INTEGRITY_RECORD_PROTOCOL_H
+#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_PRIVACY_INTEGRITY_RECORD_PROTOCOL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include "src/core/tsi/alts/crypt/gsec.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h"
+
+/**
+ * This method creates a privacy-integrity alts_grpc_record_protocol instance,
+ * given a gsec_aead_crypter instance and a flag indicating if the created
+ * instance will be used at the client or server side. The ownership of
+ * gsec_aead_crypter instance is transferred to this new object.
+ *
+ * - crypter: a gsec_aead_crypter instance used to perform AEAD decryption.
+ * - is_client: a flag indicating if the alts_grpc_record_protocol instance will
+ * be used at the client or server side.
+ * - rp: an alts_grpc_record_protocol instance to be returned from
+ * the method.
+ *
+ * This method returns TSI_OK in case of success or a specific error code in
+ * case of failure.
+ */
+tsi_result alts_grpc_privacy_integrity_record_protocol_create(
+ gsec_aead_crypter* crypter, size_t overflow_size, bool is_client,
+ bool is_protect, alts_grpc_record_protocol** rp);
+
+#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_PRIVACY_INTEGRITY_RECORD_PROTOCOL_H \
+ */
diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h
new file mode 100644
index 0000000000..d1e433dac4
--- /dev/null
+++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h
@@ -0,0 +1,91 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_H
+#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice_buffer.h>
+
+#include "src/core/tsi/transport_security_interface.h"
+
+/**
+ * This alts_grpc_record_protocol object protects and unprotects a single frame
+ * stored in grpc slice buffer with zero or minimized memory copy.
+ * Implementations of this object must be thread compatible.
+ */
+typedef struct alts_grpc_record_protocol alts_grpc_record_protocol;
+
+/**
+ * This methods performs protect operation on unprotected data and appends the
+ * protected frame to protected_slices. The caller needs to ensure the length
+ * of unprotected data plus the frame overhead is less than or equal to the
+ * maximum frame length. The input unprotected data slice buffer will be
+ * cleared, although the actual unprotected data bytes are not modified.
+ *
+ * - self: an alts_grpc_record_protocol instance.
+ * - unprotected_slices: the unprotected data to be protected.
+ * - protected_slices: slice buffer where the protected frame is appended.
+ *
+ * This method returns TSI_OK in case of success or a specific error code in
+ * case of failure.
+ */
+tsi_result alts_grpc_record_protocol_protect(
+ alts_grpc_record_protocol* self, grpc_slice_buffer* unprotected_slices,
+ grpc_slice_buffer* protected_slices);
+
+/**
+ * This methods performs unprotect operation on a full frame of protected data
+ * and appends unprotected data to unprotected_slices. It is the caller's
+ * responsibility to prepare a full frame of data before calling this method.
+ * The input protected frame slice buffer will be cleared, although the actual
+ * protected data bytes are not modified.
+ *
+ * - self: an alts_grpc_record_protocol instance.
+ * - protected_slices: a full frame of protected data in grpc slices.
+ * - unprotected_slices: slice buffer where unprotected data is appended.
+ *
+ * This method returns TSI_OK in case of success or a specific error code in
+ * case of failure.
+ */
+tsi_result alts_grpc_record_protocol_unprotect(
+ alts_grpc_record_protocol* self, grpc_slice_buffer* protected_slices,
+ grpc_slice_buffer* unprotected_slices);
+
+/**
+ * This method returns maximum allowed unprotected data size, given maximum
+ * protected frame size.
+ *
+ * - self: an alts_grpc_record_protocol instance.
+ * - max_protected_frame_size: maximum protected frame size.
+ *
+ * On success, the method returns the maximum allowed unprotected data size.
+ * Otherwise, it returns zero.
+ */
+size_t alts_grpc_record_protocol_max_unprotected_data_size(
+ const alts_grpc_record_protocol* self, size_t max_protected_frame_size);
+
+/**
+ * This method destroys an alts_grpc_record_protocol instance by de-allocating
+ * all of its occupied memory.
+ */
+void alts_grpc_record_protocol_destroy(alts_grpc_record_protocol* self);
+
+#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_H \
+ */
diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc
new file mode 100644
index 0000000000..ff91aea350
--- /dev/null
+++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc
@@ -0,0 +1,173 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+const size_t kInitialIovecBufferSize = 8;
+
+/* Makes sure iovec_buf in alts_grpc_record_protocol is large enough. */
+static void ensure_iovec_buf_size(alts_grpc_record_protocol* rp,
+ const grpc_slice_buffer* sb) {
+ GPR_ASSERT(rp != nullptr && sb != nullptr);
+ if (sb->count <= rp->iovec_buf_length) {
+ return;
+ }
+ /* At least double the iovec buffer size. */
+ rp->iovec_buf_length = GPR_MAX(sb->count, 2 * rp->iovec_buf_length);
+ rp->iovec_buf = static_cast<iovec_t*>(
+ gpr_realloc(rp->iovec_buf, rp->iovec_buf_length * sizeof(iovec_t)));
+}
+
+/* --- Implementation of methods defined in tsi_grpc_record_protocol_common.h.
+ * --- */
+
+void alts_grpc_record_protocol_convert_slice_buffer_to_iovec(
+ alts_grpc_record_protocol* rp, const grpc_slice_buffer* sb) {
+ GPR_ASSERT(rp != nullptr && sb != nullptr);
+ ensure_iovec_buf_size(rp, sb);
+ for (size_t i = 0; i < sb->count; i++) {
+ rp->iovec_buf[i].iov_base = GRPC_SLICE_START_PTR(sb->slices[i]);
+ rp->iovec_buf[i].iov_len = GRPC_SLICE_LENGTH(sb->slices[i]);
+ }
+}
+
+void alts_grpc_record_protocol_copy_slice_buffer(const grpc_slice_buffer* src,
+ unsigned char* dst) {
+ GPR_ASSERT(src != nullptr && dst != nullptr);
+ for (size_t i = 0; i < src->count; i++) {
+ size_t slice_length = GRPC_SLICE_LENGTH(src->slices[i]);
+ memcpy(dst, GRPC_SLICE_START_PTR(src->slices[i]), slice_length);
+ dst += slice_length;
+ }
+}
+
+iovec_t alts_grpc_record_protocol_get_header_iovec(
+ alts_grpc_record_protocol* rp) {
+ iovec_t header_iovec = {nullptr, 0};
+ if (rp == nullptr) {
+ return header_iovec;
+ }
+ header_iovec.iov_len = rp->header_length;
+ if (rp->header_sb.count == 1) {
+ header_iovec.iov_base = GRPC_SLICE_START_PTR(rp->header_sb.slices[0]);
+ } else {
+ /* Frame header is in multiple slices, copies the header bytes from slice
+ * buffer to a single flat buffer. */
+ alts_grpc_record_protocol_copy_slice_buffer(&rp->header_sb, rp->header_buf);
+ header_iovec.iov_base = rp->header_buf;
+ }
+ return header_iovec;
+}
+
+tsi_result alts_grpc_record_protocol_init(alts_grpc_record_protocol* rp,
+ gsec_aead_crypter* crypter,
+ size_t overflow_size, bool is_client,
+ bool is_integrity_only,
+ bool is_protect) {
+ if (rp == nullptr || crypter == nullptr) {
+ gpr_log(GPR_ERROR,
+ "Invalid nullptr arguments to alts_grpc_record_protocol init.");
+ return TSI_INVALID_ARGUMENT;
+ }
+ /* Creates alts_iovec_record_protocol. */
+ char* error_details = nullptr;
+ grpc_status_code status = alts_iovec_record_protocol_create(
+ crypter, overflow_size, is_client, is_integrity_only, is_protect,
+ &rp->iovec_rp, &error_details);
+ if (status != GRPC_STATUS_OK) {
+ gpr_log(GPR_ERROR, "Failed to create alts_iovec_record_protocol, %s.",
+ error_details);
+ gpr_free(error_details);
+ return TSI_INTERNAL_ERROR;
+ }
+ /* Allocates header slice buffer. */
+ grpc_slice_buffer_init(&rp->header_sb);
+ /* Allocates header buffer. */
+ rp->header_length = alts_iovec_record_protocol_get_header_length();
+ rp->header_buf = static_cast<unsigned char*>(gpr_malloc(rp->header_length));
+ rp->tag_length = alts_iovec_record_protocol_get_tag_length(rp->iovec_rp);
+ /* Allocates iovec buffer. */
+ rp->iovec_buf_length = kInitialIovecBufferSize;
+ rp->iovec_buf =
+ static_cast<iovec_t*>(gpr_malloc(rp->iovec_buf_length * sizeof(iovec_t)));
+ return TSI_OK;
+}
+
+/* --- Implementation of methods defined in tsi_grpc_record_protocol.h. --- */
+tsi_result alts_grpc_record_protocol_protect(
+ alts_grpc_record_protocol* self, grpc_slice_buffer* unprotected_slices,
+ grpc_slice_buffer* protected_slices) {
+ if (grpc_core::ExecCtx::Get() == nullptr || self == nullptr ||
+ self->vtable == nullptr || unprotected_slices == nullptr ||
+ protected_slices == nullptr) {
+ return TSI_INVALID_ARGUMENT;
+ }
+ if (self->vtable->protect == nullptr) {
+ return TSI_UNIMPLEMENTED;
+ }
+ return self->vtable->protect(self, unprotected_slices, protected_slices);
+}
+
+tsi_result alts_grpc_record_protocol_unprotect(
+ alts_grpc_record_protocol* self, grpc_slice_buffer* protected_slices,
+ grpc_slice_buffer* unprotected_slices) {
+ if (grpc_core::ExecCtx::Get() == nullptr || self == nullptr ||
+ self->vtable == nullptr || protected_slices == nullptr ||
+ unprotected_slices == nullptr) {
+ return TSI_INVALID_ARGUMENT;
+ }
+ if (self->vtable->unprotect == nullptr) {
+ return TSI_UNIMPLEMENTED;
+ }
+ return self->vtable->unprotect(self, protected_slices, unprotected_slices);
+}
+
+void alts_grpc_record_protocol_destroy(alts_grpc_record_protocol* self) {
+ if (self == nullptr) {
+ return;
+ }
+ if (self->vtable->destruct != nullptr) {
+ self->vtable->destruct(self);
+ }
+ alts_iovec_record_protocol_destroy(self->iovec_rp);
+ grpc_slice_buffer_destroy_internal(&self->header_sb);
+ gpr_free(self->header_buf);
+ gpr_free(self->iovec_buf);
+ gpr_free(self);
+}
+
+/* Integrity-only and privacy-integrity share the same implementation. No need
+ * to call vtable. */
+size_t alts_grpc_record_protocol_max_unprotected_data_size(
+ const alts_grpc_record_protocol* self, size_t max_protected_frame_size) {
+ if (self == nullptr) {
+ return 0;
+ }
+ return alts_iovec_record_protocol_max_unprotected_data_size(
+ self->iovec_rp, max_protected_frame_size);
+}
diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h
new file mode 100644
index 0000000000..43b8a4a2b8
--- /dev/null
+++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h
@@ -0,0 +1,100 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_COMMON_H
+#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_COMMON_H
+
+/**
+ * this file contains alts_grpc_record_protocol internals and internal-only
+ * helper functions. The public functions of alts_grpc_record_protocol are
+ * defined in the alts_grpc_record_protocol.h.
+ */
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h"
+
+/* V-table for alts_grpc_record_protocol implementations. */
+typedef struct {
+ tsi_result (*protect)(alts_grpc_record_protocol* self,
+ grpc_slice_buffer* unprotected_slices,
+ grpc_slice_buffer* protected_slices);
+ tsi_result (*unprotect)(alts_grpc_record_protocol* self,
+ grpc_slice_buffer* protected_slices,
+ grpc_slice_buffer* unprotected_slices);
+ void (*destruct)(alts_grpc_record_protocol* self);
+} alts_grpc_record_protocol_vtable;
+
+/* Main struct for alts_grpc_record_protocol implementation, shared by both
+ * integrity-only record protocol and privacy-integrity record protocol.
+ * Integrity-only record protocol has additional data elements.
+ * Privacy-integrity record protocol uses this struct directly. */
+struct alts_grpc_record_protocol {
+ const alts_grpc_record_protocol_vtable* vtable;
+ alts_iovec_record_protocol* iovec_rp;
+ grpc_slice_buffer header_sb;
+ unsigned char* header_buf;
+ size_t header_length;
+ size_t tag_length;
+ iovec_t* iovec_buf;
+ size_t iovec_buf_length;
+};
+
+/**
+ * Converts the slices of input sb into iovec_t's and puts the result into
+ * rp->iovec_buf. Note that the actual data are not copied, only
+ * pointers and lengths are copied.
+ */
+void alts_grpc_record_protocol_convert_slice_buffer_to_iovec(
+ alts_grpc_record_protocol* rp, const grpc_slice_buffer* sb);
+
+/**
+ * Copies bytes from slice buffer to destination buffer. Caller is responsible
+ * for allocating enough memory of destination buffer. This method is used for
+ * copying frame header and tag in case they are stored in multiple slices.
+ */
+void alts_grpc_record_protocol_copy_slice_buffer(const grpc_slice_buffer* src,
+ unsigned char* dst);
+
+/**
+ * This method returns an iovec object pointing to the frame header stored in
+ * rp->header_sb. If the frame header is stored in multiple slices,
+ * this method will copy the bytes in rp->header_sb to
+ * rp->header_buf, and return an iovec object pointing to
+ * rp->header_buf.
+ */
+iovec_t alts_grpc_record_protocol_get_header_iovec(
+ alts_grpc_record_protocol* rp);
+
+/**
+ * Initializes an alts_grpc_record_protocol object, given a gsec_aead_crypter
+ * instance, the overflow size of the counter in bytes, a flag indicating if the
+ * object is used for client or server side, a flag indicating if it is used for
+ * integrity-only or privacy-integrity mode, and a flag indicating if it is for
+ * protect or unprotect. The ownership of gsec_aead_crypter object is
+ * transferred to the alts_grpc_record_protocol object.
+ */
+tsi_result alts_grpc_record_protocol_init(alts_grpc_record_protocol* rp,
+ gsec_aead_crypter* crypter,
+ size_t overflow_size, bool is_client,
+ bool is_integrity_only,
+ bool is_protect);
+
+#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_COMMON_H \
+ */
diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc b/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc
new file mode 100644
index 0000000000..6a548e50dd
--- /dev/null
+++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc
@@ -0,0 +1,476 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/tsi/alts/frame_protector/alts_counter.h"
+
+struct alts_iovec_record_protocol {
+ alts_counter* ctr;
+ gsec_aead_crypter* crypter;
+ size_t tag_length;
+ bool is_integrity_only;
+ bool is_protect;
+};
+
+/* Copies error message to destination. */
+static void maybe_copy_error_msg(const char* src, char** dst) {
+ if (dst != nullptr && src != nullptr) {
+ *dst = static_cast<char*>(gpr_malloc(strlen(src) + 1));
+ memcpy(*dst, src, strlen(src) + 1);
+ }
+}
+
+/* Appends error message to destination. */
+static void maybe_append_error_msg(const char* appendix, char** dst) {
+ if (dst != nullptr && appendix != nullptr) {
+ int dst_len = static_cast<int>(strlen(*dst));
+ *dst = static_cast<char*>(realloc(*dst, dst_len + strlen(appendix) + 1));
+ assert(*dst != nullptr);
+ memcpy(*dst + dst_len, appendix, strlen(appendix) + 1);
+ }
+}
+
+/* Use little endian to interpret a string of bytes as uint32_t. */
+static uint32_t load_32_le(const unsigned char* buffer) {
+ return (((uint32_t)buffer[3]) << 24) | (((uint32_t)buffer[2]) << 16) |
+ (((uint32_t)buffer[1]) << 8) | ((uint32_t)buffer[0]);
+}
+
+/* Store uint32_t as a string of little endian bytes. */
+static void store_32_le(uint32_t value, unsigned char* buffer) {
+ buffer[3] = (unsigned char)(value >> 24) & 0xFF;
+ buffer[2] = (unsigned char)(value >> 16) & 0xFF;
+ buffer[1] = (unsigned char)(value >> 8) & 0xFF;
+ buffer[0] = (unsigned char)(value)&0xFF;
+}
+
+/* Ensures header and tag iovec have sufficient length. */
+static grpc_status_code ensure_header_and_tag_length(
+ const alts_iovec_record_protocol* rp, iovec_t header, iovec_t tag,
+ char** error_details) {
+ if (rp == nullptr) {
+ return GRPC_STATUS_FAILED_PRECONDITION;
+ }
+ if (header.iov_base == nullptr) {
+ maybe_copy_error_msg("Header is nullptr.", error_details);
+ return GRPC_STATUS_INVALID_ARGUMENT;
+ }
+ if (header.iov_len != alts_iovec_record_protocol_get_header_length()) {
+ maybe_copy_error_msg("Header length is incorrect.", error_details);
+ return GRPC_STATUS_INVALID_ARGUMENT;
+ }
+ if (tag.iov_base == nullptr) {
+ maybe_copy_error_msg("Tag is nullptr.", error_details);
+ return GRPC_STATUS_INVALID_ARGUMENT;
+ }
+ if (tag.iov_len != rp->tag_length) {
+ maybe_copy_error_msg("Tag length is incorrect.", error_details);
+ return GRPC_STATUS_INVALID_ARGUMENT;
+ }
+ return GRPC_STATUS_OK;
+}
+
+/* Increments crypter counter and checks overflow. */
+static grpc_status_code increment_counter(alts_counter* counter,
+ char** error_details) {
+ if (counter == nullptr) {
+ return GRPC_STATUS_FAILED_PRECONDITION;
+ }
+ bool is_overflow = false;
+ grpc_status_code status =
+ alts_counter_increment(counter, &is_overflow, error_details);
+ if (status != GRPC_STATUS_OK) {
+ return status;
+ }
+ if (is_overflow) {
+ maybe_copy_error_msg("Crypter counter is overflowed.", error_details);
+ return GRPC_STATUS_INTERNAL;
+ }
+ return GRPC_STATUS_OK;
+}
+
+/* Given an array of iovec, computes the total length of buffer. */
+static size_t get_total_length(const iovec_t* vec, size_t vec_length) {
+ size_t total_length = 0;
+ for (size_t i = 0; i < vec_length; ++i) {
+ total_length += vec[i].iov_len;
+ }
+ return total_length;
+}
+
+/* Writes frame header given data and tag length. */
+static grpc_status_code write_frame_header(size_t data_length,
+ unsigned char* header,
+ char** error_details) {
+ if (header == nullptr) {
+ maybe_copy_error_msg("Header is nullptr.", error_details);
+ return GRPC_STATUS_FAILED_PRECONDITION;
+ }
+ size_t frame_length = kZeroCopyFrameMessageTypeFieldSize + data_length;
+ store_32_le(static_cast<uint32_t>(frame_length), header);
+ store_32_le(kZeroCopyFrameMessageType,
+ header + kZeroCopyFrameLengthFieldSize);
+ return GRPC_STATUS_OK;
+}
+
+/* Verifies frame header given protected data length. */
+static grpc_status_code verify_frame_header(size_t data_length,
+ unsigned char* header,
+ char** error_details) {
+ if (header == nullptr) {
+ maybe_copy_error_msg("Header is nullptr.", error_details);
+ return GRPC_STATUS_FAILED_PRECONDITION;
+ }
+ size_t frame_length = load_32_le(header);
+ if (frame_length != kZeroCopyFrameMessageTypeFieldSize + data_length) {
+ maybe_copy_error_msg("Bad frame length.", error_details);
+ return GRPC_STATUS_INTERNAL;
+ }
+ size_t message_type = load_32_le(header + kZeroCopyFrameLengthFieldSize);
+ if (message_type != kZeroCopyFrameMessageType) {
+ maybe_copy_error_msg("Unsupported message type.", error_details);
+ return GRPC_STATUS_INTERNAL;
+ }
+ return GRPC_STATUS_OK;
+}
+
+/* --- alts_iovec_record_protocol methods implementation. --- */
+
+size_t alts_iovec_record_protocol_get_header_length() {
+ return kZeroCopyFrameHeaderSize;
+}
+
+size_t alts_iovec_record_protocol_get_tag_length(
+ const alts_iovec_record_protocol* rp) {
+ if (rp != nullptr) {
+ return rp->tag_length;
+ }
+ return 0;
+}
+
+size_t alts_iovec_record_protocol_max_unprotected_data_size(
+ const alts_iovec_record_protocol* rp, size_t max_protected_frame_size) {
+ if (rp == nullptr) {
+ return 0;
+ }
+ size_t overhead_bytes_size =
+ kZeroCopyFrameMessageTypeFieldSize + rp->tag_length;
+ if (max_protected_frame_size <= overhead_bytes_size) return 0;
+ return max_protected_frame_size - overhead_bytes_size;
+}
+
+grpc_status_code alts_iovec_record_protocol_integrity_only_protect(
+ alts_iovec_record_protocol* rp, const iovec_t* unprotected_vec,
+ size_t unprotected_vec_length, iovec_t header, iovec_t tag,
+ char** error_details) {
+ /* Input sanity checks. */
+ if (rp == nullptr) {
+ maybe_copy_error_msg("Input iovec_record_protocol is nullptr.",
+ error_details);
+ return GRPC_STATUS_INVALID_ARGUMENT;
+ }
+ if (!rp->is_integrity_only) {
+ maybe_copy_error_msg(
+ "Integrity-only operations are not allowed for this object.",
+ error_details);
+ return GRPC_STATUS_FAILED_PRECONDITION;
+ }
+ if (!rp->is_protect) {
+ maybe_copy_error_msg("Protect operations are not allowed for this object.",
+ error_details);
+ return GRPC_STATUS_FAILED_PRECONDITION;
+ }
+ grpc_status_code status =
+ ensure_header_and_tag_length(rp, header, tag, error_details);
+ if (status != GRPC_STATUS_OK) {
+ return status;
+ }
+ /* Unprotected data should not be zero length. */
+ size_t data_length =
+ get_total_length(unprotected_vec, unprotected_vec_length);
+ /* Sets frame header. */
+ status = write_frame_header(data_length + rp->tag_length,
+ static_cast<unsigned char*>(header.iov_base),
+ error_details);
+ if (status != GRPC_STATUS_OK) {
+ return status;
+ }
+ /* Computes frame tag by calling AEAD crypter. */
+ size_t bytes_written = 0;
+ status = gsec_aead_crypter_encrypt_iovec(
+ rp->crypter, alts_counter_get_counter(rp->ctr),
+ alts_counter_get_size(rp->ctr), unprotected_vec, unprotected_vec_length,
+ /* plaintext_vec = */ nullptr, /* plaintext_vec_length = */ 0, tag,
+ &bytes_written, error_details);
+ if (status != GRPC_STATUS_OK) {
+ return status;
+ }
+ if (bytes_written != rp->tag_length) {
+ maybe_copy_error_msg("Bytes written expects to be the same as tag length.",
+ error_details);
+ return GRPC_STATUS_INTERNAL;
+ }
+ /* Increments the crypter counter. */
+ return increment_counter(rp->ctr, error_details);
+}
+
+grpc_status_code alts_iovec_record_protocol_integrity_only_unprotect(
+ alts_iovec_record_protocol* rp, const iovec_t* protected_vec,
+ size_t protected_vec_length, iovec_t header, iovec_t tag,
+ char** error_details) {
+ /* Input sanity checks. */
+ if (rp == nullptr) {
+ maybe_copy_error_msg("Input iovec_record_protocol is nullptr.",
+ error_details);
+ return GRPC_STATUS_INVALID_ARGUMENT;
+ }
+ if (!rp->is_integrity_only) {
+ maybe_copy_error_msg(
+ "Integrity-only operations are not allowed for this object.",
+ error_details);
+ return GRPC_STATUS_FAILED_PRECONDITION;
+ }
+ if (rp->is_protect) {
+ maybe_copy_error_msg(
+ "Unprotect operations are not allowed for this object.", error_details);
+ return GRPC_STATUS_FAILED_PRECONDITION;
+ }
+ grpc_status_code status =
+ ensure_header_and_tag_length(rp, header, tag, error_details);
+ if (status != GRPC_STATUS_OK) return status;
+ /* Protected data should not be zero length. */
+ size_t data_length = get_total_length(protected_vec, protected_vec_length);
+ /* Verifies frame header. */
+ status = verify_frame_header(data_length + rp->tag_length,
+ static_cast<unsigned char*>(header.iov_base),
+ error_details);
+ if (status != GRPC_STATUS_OK) {
+ return status;
+ }
+ /* Verifies frame tag by calling AEAD crypter. */
+ iovec_t plaintext = {nullptr, 0};
+ size_t bytes_written = 0;
+ status = gsec_aead_crypter_decrypt_iovec(
+ rp->crypter, alts_counter_get_counter(rp->ctr),
+ alts_counter_get_size(rp->ctr), protected_vec, protected_vec_length, &tag,
+ 1, plaintext, &bytes_written, error_details);
+ if (status != GRPC_STATUS_OK || bytes_written != 0) {
+ maybe_append_error_msg(" Frame tag verification failed.", error_details);
+ return GRPC_STATUS_INTERNAL;
+ }
+ /* Increments the crypter counter. */
+ return increment_counter(rp->ctr, error_details);
+}
+
+grpc_status_code alts_iovec_record_protocol_privacy_integrity_protect(
+ alts_iovec_record_protocol* rp, const iovec_t* unprotected_vec,
+ size_t unprotected_vec_length, iovec_t protected_frame,
+ char** error_details) {
+ /* Input sanity checks. */
+ if (rp == nullptr) {
+ maybe_copy_error_msg("Input iovec_record_protocol is nullptr.",
+ error_details);
+ return GRPC_STATUS_INVALID_ARGUMENT;
+ }
+ if (rp->is_integrity_only) {
+ maybe_copy_error_msg(
+ "Privacy-integrity operations are not allowed for this object.",
+ error_details);
+ return GRPC_STATUS_FAILED_PRECONDITION;
+ }
+ if (!rp->is_protect) {
+ maybe_copy_error_msg("Protect operations are not allowed for this object.",
+ error_details);
+ return GRPC_STATUS_FAILED_PRECONDITION;
+ }
+ /* Unprotected data should not be zero length. */
+ size_t data_length =
+ get_total_length(unprotected_vec, unprotected_vec_length);
+ /* Ensures protected frame iovec has sufficient size. */
+ if (protected_frame.iov_base == nullptr) {
+ maybe_copy_error_msg("Protected frame is nullptr.", error_details);
+ return GRPC_STATUS_INVALID_ARGUMENT;
+ }
+ if (protected_frame.iov_len !=
+ alts_iovec_record_protocol_get_header_length() + data_length +
+ rp->tag_length) {
+ maybe_copy_error_msg("Protected frame size is incorrect.", error_details);
+ return GRPC_STATUS_INVALID_ARGUMENT;
+ }
+ /* Writer frame header. */
+ grpc_status_code status = write_frame_header(
+ data_length + rp->tag_length,
+ static_cast<unsigned char*>(protected_frame.iov_base), error_details);
+ if (status != GRPC_STATUS_OK) {
+ return status;
+ }
+ /* Encrypt unprotected data by calling AEAD crypter. */
+ unsigned char* ciphertext_buffer =
+ static_cast<unsigned char*>(protected_frame.iov_base) +
+ alts_iovec_record_protocol_get_header_length();
+ iovec_t ciphertext = {ciphertext_buffer, data_length + rp->tag_length};
+ size_t bytes_written = 0;
+ status = gsec_aead_crypter_encrypt_iovec(
+ rp->crypter, alts_counter_get_counter(rp->ctr),
+ alts_counter_get_size(rp->ctr), /* aad_vec = */ nullptr,
+ /* aad_vec_length = */ 0, unprotected_vec, unprotected_vec_length,
+ ciphertext, &bytes_written, error_details);
+ if (status != GRPC_STATUS_OK) {
+ return status;
+ }
+ if (bytes_written != data_length + rp->tag_length) {
+ maybe_copy_error_msg(
+ "Bytes written expects to be data length plus tag length.",
+ error_details);
+ return GRPC_STATUS_INTERNAL;
+ }
+ /* Increments the crypter counter. */
+ return increment_counter(rp->ctr, error_details);
+}
+
+grpc_status_code alts_iovec_record_protocol_privacy_integrity_unprotect(
+ alts_iovec_record_protocol* rp, iovec_t header,
+ const iovec_t* protected_vec, size_t protected_vec_length,
+ iovec_t unprotected_data, char** error_details) {
+ /* Input sanity checks. */
+ if (rp == nullptr) {
+ maybe_copy_error_msg("Input iovec_record_protocol is nullptr.",
+ error_details);
+ return GRPC_STATUS_INVALID_ARGUMENT;
+ }
+ if (rp->is_integrity_only) {
+ maybe_copy_error_msg(
+ "Privacy-integrity operations are not allowed for this object.",
+ error_details);
+ return GRPC_STATUS_FAILED_PRECONDITION;
+ }
+ if (rp->is_protect) {
+ maybe_copy_error_msg(
+ "Unprotect operations are not allowed for this object.", error_details);
+ return GRPC_STATUS_FAILED_PRECONDITION;
+ }
+ /* Protected data size should be no less than tag size. */
+ size_t protected_data_length =
+ get_total_length(protected_vec, protected_vec_length);
+ if (protected_data_length < rp->tag_length) {
+ maybe_copy_error_msg(
+ "Protected data length should be more than the tag length.",
+ error_details);
+ return GRPC_STATUS_INVALID_ARGUMENT;
+ }
+ /* Ensures header has sufficient size. */
+ if (header.iov_base == nullptr) {
+ maybe_copy_error_msg("Header is nullptr.", error_details);
+ return GRPC_STATUS_INVALID_ARGUMENT;
+ }
+ if (header.iov_len != alts_iovec_record_protocol_get_header_length()) {
+ maybe_copy_error_msg("Header length is incorrect.", error_details);
+ return GRPC_STATUS_INVALID_ARGUMENT;
+ }
+ /* Ensures unprotected data iovec has sufficient size. */
+ if (unprotected_data.iov_len != protected_data_length - rp->tag_length) {
+ maybe_copy_error_msg("Unprotected data size is incorrect.", error_details);
+ return GRPC_STATUS_INVALID_ARGUMENT;
+ }
+ /* Verify frame header. */
+ grpc_status_code status = verify_frame_header(
+ protected_data_length, static_cast<unsigned char*>(header.iov_base),
+ error_details);
+ if (status != GRPC_STATUS_OK) {
+ return status;
+ }
+ /* Decrypt protected data by calling AEAD crypter. */
+ size_t bytes_written = 0;
+ status = gsec_aead_crypter_decrypt_iovec(
+ rp->crypter, alts_counter_get_counter(rp->ctr),
+ alts_counter_get_size(rp->ctr), /* aad_vec = */ nullptr,
+ /* aad_vec_length = */ 0, protected_vec, protected_vec_length,
+ unprotected_data, &bytes_written, error_details);
+ if (status != GRPC_STATUS_OK) {
+ maybe_append_error_msg(" Frame decryption failed.", error_details);
+ return GRPC_STATUS_INTERNAL;
+ }
+ if (bytes_written != protected_data_length - rp->tag_length) {
+ maybe_copy_error_msg(
+ "Bytes written expects to be protected data length minus tag length.",
+ error_details);
+ return GRPC_STATUS_INTERNAL;
+ }
+ /* Increments the crypter counter. */
+ return increment_counter(rp->ctr, error_details);
+}
+
+grpc_status_code alts_iovec_record_protocol_create(
+ gsec_aead_crypter* crypter, size_t overflow_size, bool is_client,
+ bool is_integrity_only, bool is_protect, alts_iovec_record_protocol** rp,
+ char** error_details) {
+ if (crypter == nullptr || rp == nullptr) {
+ maybe_copy_error_msg(
+ "Invalid nullptr arguments to alts_iovec_record_protocol create.",
+ error_details);
+ return GRPC_STATUS_INVALID_ARGUMENT;
+ }
+ alts_iovec_record_protocol* impl = static_cast<alts_iovec_record_protocol*>(
+ gpr_zalloc(sizeof(alts_iovec_record_protocol)));
+ /* Gets counter length. */
+ size_t counter_length = 0;
+ grpc_status_code status =
+ gsec_aead_crypter_nonce_length(crypter, &counter_length, error_details);
+ if (status != GRPC_STATUS_OK) {
+ goto cleanup;
+ }
+ /* Creates counters. */
+ status =
+ alts_counter_create(is_protect ? !is_client : is_client, counter_length,
+ overflow_size, &impl->ctr, error_details);
+ if (status != GRPC_STATUS_OK) {
+ goto cleanup;
+ }
+ /* Gets tag length. */
+ status =
+ gsec_aead_crypter_tag_length(crypter, &impl->tag_length, error_details);
+ if (status != GRPC_STATUS_OK) {
+ goto cleanup;
+ }
+ impl->crypter = crypter;
+ impl->is_integrity_only = is_integrity_only;
+ impl->is_protect = is_protect;
+ *rp = impl;
+ return GRPC_STATUS_OK;
+cleanup:
+ alts_counter_destroy(impl->ctr);
+ gpr_free(impl);
+ return GRPC_STATUS_FAILED_PRECONDITION;
+}
+
+void alts_iovec_record_protocol_destroy(alts_iovec_record_protocol* rp) {
+ if (rp != nullptr) {
+ alts_counter_destroy(rp->ctr);
+ gsec_aead_crypter_destroy(rp->crypter);
+ gpr_free(rp);
+ }
+}
diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h b/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h
new file mode 100644
index 0000000000..0b7d1bf5bf
--- /dev/null
+++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h
@@ -0,0 +1,199 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_IOVEC_RECORD_PROTOCOL_H
+#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_IOVEC_RECORD_PROTOCOL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include "src/core/tsi/alts/crypt/gsec.h"
+
+constexpr size_t kZeroCopyFrameMessageType = 0x06;
+constexpr size_t kZeroCopyFrameLengthFieldSize = 4;
+constexpr size_t kZeroCopyFrameMessageTypeFieldSize = 4;
+constexpr size_t kZeroCopyFrameHeaderSize =
+ kZeroCopyFrameLengthFieldSize + kZeroCopyFrameMessageTypeFieldSize;
+
+// Limit k on number of frames such that at most 2^(8 * k) frames can be sent.
+constexpr size_t kAltsRecordProtocolRekeyFrameLimit = 8;
+constexpr size_t kAltsRecordProtocolFrameLimit = 5;
+
+/* An implementation of alts record protocol. The API is thread-compatible. */
+
+typedef struct iovec iovec_t;
+
+typedef struct alts_iovec_record_protocol alts_iovec_record_protocol;
+
+/**
+ * This method gets the length of record protocol frame header.
+ */
+size_t alts_iovec_record_protocol_get_header_length();
+
+/**
+ * This method gets the length of record protocol frame tag.
+ *
+ * - rp: an alts_iovec_record_protocol instance.
+ *
+ * On success, the method returns the length of record protocol frame tag.
+ * Otherwise, it returns zero.
+ */
+size_t alts_iovec_record_protocol_get_tag_length(
+ const alts_iovec_record_protocol* rp);
+
+/**
+ * This method returns maximum allowed unprotected data size, given maximum
+ * protected frame size.
+ *
+ * - rp: an alts_iovec_record_protocol instance.
+ * - max_protected_frame_size: maximum protected frame size.
+ *
+ * On success, the method returns the maximum allowed unprotected data size.
+ * Otherwise, it returns zero.
+ */
+size_t alts_iovec_record_protocol_max_unprotected_data_size(
+ const alts_iovec_record_protocol* rp, size_t max_protected_frame_size);
+
+/**
+ * This method performs integrity-only protect operation on a
+ * alts_iovec_record_protocol instance, i.e., compute frame header and tag. The
+ * caller needs to allocate the memory for header and tag prior to calling this
+ * method.
+ *
+ * - rp: an alts_iovec_record_protocol instance.
+ * - unprotected_vec: an iovec array containing unprotected data.
+ * - unprotected_vec_length: the array length of unprotected_vec.
+ * - header: an iovec containing the output frame header.
+ * - tag: an iovec containing the output frame tag.
+ * - error_details: a buffer containing an error message if the method does not
+ * function correctly. It is OK to pass nullptr into error_details.
+ *
+ * On success, the method returns GRPC_STATUS_OK. Otherwise, it returns an
+ * error status code along with its details specified in error_details (if
+ * error_details is not nullptr).
+ */
+grpc_status_code alts_iovec_record_protocol_integrity_only_protect(
+ alts_iovec_record_protocol* rp, const iovec_t* unprotected_vec,
+ size_t unprotected_vec_length, iovec_t header, iovec_t tag,
+ char** error_details);
+
+/**
+ * This method performs integrity-only unprotect operation on a
+ * alts_iovec_record_protocol instance, i.e., verify frame header and tag.
+ *
+ * - rp: an alts_iovec_record_protocol instance.
+ * - protected_vec: an iovec array containing protected data.
+ * - protected_vec_length: the array length of protected_vec.
+ * - header: an iovec containing the frame header.
+ * - tag: an iovec containing the frame tag.
+ * - error_details: a buffer containing an error message if the method does not
+ * function correctly. It is OK to pass nullptr into error_details.
+ *
+ * On success, the method returns GRPC_STATUS_OK. Otherwise, it returns an
+ * error status code along with its details specified in error_details (if
+ * error_details is not nullptr).
+ */
+grpc_status_code alts_iovec_record_protocol_integrity_only_unprotect(
+ alts_iovec_record_protocol* rp, const iovec_t* protected_vec,
+ size_t protected_vec_length, iovec_t header, iovec_t tag,
+ char** error_details);
+
+/**
+ * This method performs privacy-integrity protect operation on a
+ * alts_iovec_record_protocol instance, i.e., compute a protected frame. The
+ * caller needs to allocate the memory for the protected frame prior to calling
+ * this method.
+ *
+ * - rp: an alts_iovec_record_protocol instance.
+ * - unprotected_vec: an iovec array containing unprotected data.
+ * - unprotected_vec_length: the array length of unprotected_vec.
+ * - protected_frame: an iovec containing the output protected frame.
+ * - error_details: a buffer containing an error message if the method does not
+ * function correctly. It is OK to pass nullptr into error_details.
+ *
+ * On success, the method returns GRPC_STATUS_OK. Otherwise, it returns an
+ * error status code along with its details specified in error_details (if
+ * error_details is not nullptr).
+ */
+grpc_status_code alts_iovec_record_protocol_privacy_integrity_protect(
+ alts_iovec_record_protocol* rp, const iovec_t* unprotected_vec,
+ size_t unprotected_vec_length, iovec_t protected_frame,
+ char** error_details);
+
+/**
+ * This method performs privacy-integrity unprotect operation on a
+ * alts_iovec_record_protocol instance given a full protected frame, i.e.,
+ * compute the unprotected data. The caller needs to allocated the memory for
+ * the unprotected data prior to calling this method.
+ *
+ * - rp: an alts_iovec_record_protocol instance.
+ * - header: an iovec containing the frame header.
+ * - protected_vec: an iovec array containing protected data including the tag.
+ * - protected_vec_length: the array length of protected_vec.
+ * - unprotected_data: an iovec containing the output unprotected data.
+ * - error_details: a buffer containing an error message if the method does not
+ * function correctly. It is OK to pass nullptr into error_details.
+ *
+ * On success, the method returns GRPC_STATUS_OK. Otherwise, it returns an
+ * error status code along with its details specified in error_details (if
+ * error_details is not nullptr).
+ */
+grpc_status_code alts_iovec_record_protocol_privacy_integrity_unprotect(
+ alts_iovec_record_protocol* rp, iovec_t header,
+ const iovec_t* protected_vec, size_t protected_vec_length,
+ iovec_t unprotected_data, char** error_details);
+
+/**
+ * This method creates an alts_iovec_record_protocol instance, given a
+ * gsec_aead_crypter instance, a flag indicating if the created instance will be
+ * used at the client or server side, and a flag indicating if the created
+ * instance will be used for integrity-only mode or privacy-integrity mode. The
+ * ownership of gsec_aead_crypter instance is transferred to this new object.
+ *
+ * - crypter: a gsec_aead_crypter instance used to perform AEAD decryption.
+ * - overflow_size: overflow size of counter in bytes.
+ * - is_client: a flag indicating if the alts_iovec_record_protocol instance
+ * will be used at the client or server side.
+ * - is_integrity_only: a flag indicating if the alts_iovec_record_protocol
+ * instance will be used for integrity-only or privacy-integrity mode.
+ * - is_protect: a flag indicating if the alts_grpc_record_protocol instance
+ * will be used for protect or unprotect.
+ * - rp: an alts_iovec_record_protocol instance to be returned from
+ * the method.
+ * - error_details: a buffer containing an error message if the method does not
+ * function correctly. It is OK to pass nullptr into error_details.
+ *
+ * On success, the method returns GRPC_STATUS_OK. Otherwise, it returns an
+ * error status code along with its details specified in error_details (if
+ * error_details is not nullptr).
+ */
+grpc_status_code alts_iovec_record_protocol_create(
+ gsec_aead_crypter* crypter, size_t overflow_size, bool is_client,
+ bool is_integrity_only, bool is_protect, alts_iovec_record_protocol** rp,
+ char** error_details);
+
+/**
+ * This method destroys an alts_iovec_record_protocol instance by de-allocating
+ * all of its occupied memory. A gsec_aead_crypter instance passed in at
+ * gsec_alts_crypter instance creation time will be destroyed in this method.
+ */
+void alts_iovec_record_protocol_destroy(alts_iovec_record_protocol* rp);
+
+#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_IOVEC_RECORD_PROTOCOL_H \
+ */
diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc b/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc
new file mode 100644
index 0000000000..8c764961b3
--- /dev/null
+++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc
@@ -0,0 +1,295 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/tsi/alts/crypt/gsec.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h"
+#include "src/core/tsi/transport_security_grpc.h"
+
+constexpr size_t kMinFrameLength = 1024;
+constexpr size_t kDefaultFrameLength = 16 * 1024;
+constexpr size_t kMaxFrameLength = 1024 * 1024;
+
+/**
+ * Main struct for alts_zero_copy_grpc_protector.
+ * We choose to have two alts_grpc_record_protocol objects and two sets of slice
+ * buffers: one for protect and the other for unprotect, so that protect and
+ * unprotect can be executed in parallel. Implementations of this object must be
+ * thread compatible.
+ */
+typedef struct alts_zero_copy_grpc_protector {
+ tsi_zero_copy_grpc_protector base;
+ alts_grpc_record_protocol* record_protocol;
+ alts_grpc_record_protocol* unrecord_protocol;
+ size_t max_protected_frame_size;
+ size_t max_unprotected_data_size;
+ grpc_slice_buffer unprotected_staging_sb;
+ grpc_slice_buffer protected_sb;
+ grpc_slice_buffer protected_staging_sb;
+ uint32_t parsed_frame_size;
+} alts_zero_copy_grpc_protector;
+
+/**
+ * Given a slice buffer, parses the first 4 bytes little-endian unsigned frame
+ * size and returns the total frame size including the frame field. Caller
+ * needs to make sure the input slice buffer has at least 4 bytes. Returns true
+ * on success and false on failure.
+ */
+static bool read_frame_size(const grpc_slice_buffer* sb,
+ uint32_t* total_frame_size) {
+ if (sb == nullptr || sb->length < kZeroCopyFrameLengthFieldSize) {
+ return false;
+ }
+ uint8_t frame_size_buffer[kZeroCopyFrameLengthFieldSize];
+ uint8_t* buf = frame_size_buffer;
+ /* Copies the first 4 bytes to a temporary buffer. */
+ size_t remaining = kZeroCopyFrameLengthFieldSize;
+ for (size_t i = 0; i < sb->count; i++) {
+ size_t slice_length = GRPC_SLICE_LENGTH(sb->slices[i]);
+ if (remaining <= slice_length) {
+ memcpy(buf, GRPC_SLICE_START_PTR(sb->slices[i]), remaining);
+ remaining = 0;
+ break;
+ } else {
+ memcpy(buf, GRPC_SLICE_START_PTR(sb->slices[i]), slice_length);
+ buf += slice_length;
+ remaining -= slice_length;
+ }
+ }
+ GPR_ASSERT(remaining == 0);
+ /* Gets little-endian frame size. */
+ uint32_t frame_size = (((uint32_t)frame_size_buffer[3]) << 24) |
+ (((uint32_t)frame_size_buffer[2]) << 16) |
+ (((uint32_t)frame_size_buffer[1]) << 8) |
+ ((uint32_t)frame_size_buffer[0]);
+ if (frame_size > kMaxFrameLength) {
+ gpr_log(GPR_ERROR, "Frame size is larger than maximum frame size");
+ return false;
+ }
+ /* Returns frame size including frame length field. */
+ *total_frame_size =
+ static_cast<uint32_t>(frame_size + kZeroCopyFrameLengthFieldSize);
+ return true;
+}
+
+/**
+ * Creates an alts_grpc_record_protocol object, given key, key size, and flags
+ * to indicate whether the record_protocol object uses the rekeying AEAD,
+ * whether the object is for client or server, whether the object is for
+ * integrity-only or privacy-integrity mode, and whether the object is is used
+ * for protect or unprotect.
+ */
+static tsi_result create_alts_grpc_record_protocol(
+ const uint8_t* key, size_t key_size, bool is_rekey, bool is_client,
+ bool is_integrity_only, bool is_protect,
+ alts_grpc_record_protocol** record_protocol) {
+ if (key == nullptr || record_protocol == nullptr) {
+ return TSI_INVALID_ARGUMENT;
+ }
+ grpc_status_code status;
+ gsec_aead_crypter* crypter = nullptr;
+ char* error_details = nullptr;
+ status = gsec_aes_gcm_aead_crypter_create(key, key_size, kAesGcmNonceLength,
+ kAesGcmTagLength, is_rekey,
+ &crypter, &error_details);
+ if (status != GRPC_STATUS_OK) {
+ gpr_log(GPR_ERROR, "Failed to create AEAD crypter, %s", error_details);
+ gpr_free(error_details);
+ return TSI_INTERNAL_ERROR;
+ }
+ size_t overflow_limit = is_rekey ? kAltsRecordProtocolRekeyFrameLimit
+ : kAltsRecordProtocolFrameLimit;
+ /* Creates alts_grpc_record_protocol with AEAD crypter ownership transferred.
+ */
+ tsi_result result =
+ is_integrity_only
+ ? alts_grpc_integrity_only_record_protocol_create(
+ crypter, overflow_limit, is_client, is_protect, record_protocol)
+ : alts_grpc_privacy_integrity_record_protocol_create(
+ crypter, overflow_limit, is_client, is_protect,
+ record_protocol);
+ if (result != TSI_OK) {
+ gsec_aead_crypter_destroy(crypter);
+ return result;
+ }
+ return TSI_OK;
+}
+
+/* --- tsi_zero_copy_grpc_protector methods implementation. --- */
+
+static tsi_result alts_zero_copy_grpc_protector_protect(
+ tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* unprotected_slices,
+ grpc_slice_buffer* protected_slices) {
+ if (self == nullptr || unprotected_slices == nullptr ||
+ protected_slices == nullptr) {
+ gpr_log(GPR_ERROR, "Invalid nullptr arguments to zero-copy grpc protect.");
+ return TSI_INVALID_ARGUMENT;
+ }
+ alts_zero_copy_grpc_protector* protector =
+ reinterpret_cast<alts_zero_copy_grpc_protector*>(self);
+ /* Calls alts_grpc_record_protocol protect repeatly. */
+ while (unprotected_slices->length > protector->max_unprotected_data_size) {
+ grpc_slice_buffer_move_first(unprotected_slices,
+ protector->max_unprotected_data_size,
+ &protector->unprotected_staging_sb);
+ tsi_result status = alts_grpc_record_protocol_protect(
+ protector->record_protocol, &protector->unprotected_staging_sb,
+ protected_slices);
+ if (status != TSI_OK) {
+ return status;
+ }
+ }
+ return alts_grpc_record_protocol_protect(
+ protector->record_protocol, unprotected_slices, protected_slices);
+}
+
+static tsi_result alts_zero_copy_grpc_protector_unprotect(
+ tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* protected_slices,
+ grpc_slice_buffer* unprotected_slices) {
+ if (self == nullptr || unprotected_slices == nullptr ||
+ protected_slices == nullptr) {
+ gpr_log(GPR_ERROR,
+ "Invalid nullptr arguments to zero-copy grpc unprotect.");
+ return TSI_INVALID_ARGUMENT;
+ }
+ alts_zero_copy_grpc_protector* protector =
+ reinterpret_cast<alts_zero_copy_grpc_protector*>(self);
+ grpc_slice_buffer_move_into(protected_slices, &protector->protected_sb);
+ /* Keep unprotecting each frame if possible. */
+ while (protector->protected_sb.length >= kZeroCopyFrameLengthFieldSize) {
+ if (protector->parsed_frame_size == 0) {
+ /* We have not parsed frame size yet. Parses frame size. */
+ if (!read_frame_size(&protector->protected_sb,
+ &protector->parsed_frame_size)) {
+ grpc_slice_buffer_reset_and_unref_internal(&protector->protected_sb);
+ return TSI_DATA_CORRUPTED;
+ }
+ }
+ if (protector->protected_sb.length < protector->parsed_frame_size) break;
+ /* At this point, protected_sb contains at least one frame of data. */
+ tsi_result status;
+ if (protector->protected_sb.length == protector->parsed_frame_size) {
+ status = alts_grpc_record_protocol_unprotect(protector->unrecord_protocol,
+ &protector->protected_sb,
+ unprotected_slices);
+ } else {
+ grpc_slice_buffer_move_first(&protector->protected_sb,
+ protector->parsed_frame_size,
+ &protector->protected_staging_sb);
+ status = alts_grpc_record_protocol_unprotect(
+ protector->unrecord_protocol, &protector->protected_staging_sb,
+ unprotected_slices);
+ }
+ protector->parsed_frame_size = 0;
+ if (status != TSI_OK) {
+ grpc_slice_buffer_reset_and_unref_internal(&protector->protected_sb);
+ return status;
+ }
+ }
+ return TSI_OK;
+}
+
+static void alts_zero_copy_grpc_protector_destroy(
+ tsi_zero_copy_grpc_protector* self) {
+ if (self == nullptr) {
+ return;
+ }
+ alts_zero_copy_grpc_protector* protector =
+ reinterpret_cast<alts_zero_copy_grpc_protector*>(self);
+ alts_grpc_record_protocol_destroy(protector->record_protocol);
+ alts_grpc_record_protocol_destroy(protector->unrecord_protocol);
+ grpc_slice_buffer_destroy_internal(&protector->unprotected_staging_sb);
+ grpc_slice_buffer_destroy_internal(&protector->protected_sb);
+ grpc_slice_buffer_destroy_internal(&protector->protected_staging_sb);
+ gpr_free(protector);
+}
+
+static const tsi_zero_copy_grpc_protector_vtable
+ alts_zero_copy_grpc_protector_vtable = {
+ alts_zero_copy_grpc_protector_protect,
+ alts_zero_copy_grpc_protector_unprotect,
+ alts_zero_copy_grpc_protector_destroy};
+
+tsi_result alts_zero_copy_grpc_protector_create(
+ const uint8_t* key, size_t key_size, bool is_rekey, bool is_client,
+ bool is_integrity_only, size_t* max_protected_frame_size,
+ tsi_zero_copy_grpc_protector** protector) {
+ if (grpc_core::ExecCtx::Get() == nullptr || key == nullptr ||
+ protector == nullptr) {
+ gpr_log(
+ GPR_ERROR,
+ "Invalid nullptr arguments to alts_zero_copy_grpc_protector create.");
+ return TSI_INVALID_ARGUMENT;
+ }
+ /* Creates alts_zero_copy_protector. */
+ alts_zero_copy_grpc_protector* impl =
+ static_cast<alts_zero_copy_grpc_protector*>(
+ gpr_zalloc(sizeof(alts_zero_copy_grpc_protector)));
+ /* Creates alts_grpc_record_protocol objects. */
+ tsi_result status = create_alts_grpc_record_protocol(
+ key, key_size, is_rekey, is_client, is_integrity_only,
+ /*is_protect=*/true, &impl->record_protocol);
+ if (status == TSI_OK) {
+ status = create_alts_grpc_record_protocol(
+ key, key_size, is_rekey, is_client, is_integrity_only,
+ /*is_protect=*/false, &impl->unrecord_protocol);
+ if (status == TSI_OK) {
+ /* Sets maximum frame size. */
+ size_t max_protected_frame_size_to_set = kDefaultFrameLength;
+ if (max_protected_frame_size != nullptr) {
+ *max_protected_frame_size =
+ GPR_MIN(*max_protected_frame_size, kMaxFrameLength);
+ *max_protected_frame_size =
+ GPR_MAX(*max_protected_frame_size, kMinFrameLength);
+ max_protected_frame_size_to_set = *max_protected_frame_size;
+ }
+ impl->max_protected_frame_size = max_protected_frame_size_to_set;
+ impl->max_unprotected_data_size =
+ alts_grpc_record_protocol_max_unprotected_data_size(
+ impl->record_protocol, max_protected_frame_size_to_set);
+ GPR_ASSERT(impl->max_unprotected_data_size > 0);
+ /* Allocates internal slice buffers. */
+ grpc_slice_buffer_init(&impl->unprotected_staging_sb);
+ grpc_slice_buffer_init(&impl->protected_sb);
+ grpc_slice_buffer_init(&impl->protected_staging_sb);
+ impl->parsed_frame_size = 0;
+ impl->base.vtable = &alts_zero_copy_grpc_protector_vtable;
+ *protector = &impl->base;
+ return TSI_OK;
+ }
+ }
+
+ /* Cleanup if create failed. */
+ alts_grpc_record_protocol_destroy(impl->record_protocol);
+ alts_grpc_record_protocol_destroy(impl->unrecord_protocol);
+ gpr_free(impl);
+ return TSI_INTERNAL_ERROR;
+}
diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h b/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h
new file mode 100644
index 0000000000..71e953cfc1
--- /dev/null
+++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_ZERO_COPY_GRPC_PROTECTOR_H
+#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_ZERO_COPY_GRPC_PROTECTOR_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include "src/core/tsi/transport_security_grpc.h"
+
+/**
+ * This method creates an ALTS zero-copy grpc protector.
+ *
+ * - key: a symmetric key used to seal/unseal frames.
+ * - key_size: the size of symmetric key.
+ * - is_rekey: use rekeying AEAD crypter.
+ * - is_client: a flag indicating if the protector will be used at client or
+ * server side.
+ * - is_integrity_only: a flag indicating if the protector instance will be
+ * used for integrity-only or privacy-integrity mode.
+ * - max_protected_frame_size: an in/out parameter indicating max frame size
+ * to be used by the protector. If it is nullptr, the default frame size will
+ * be used. Otherwise, the provided frame size will be adjusted (if not
+ * falling into a valid frame range) and used.
+ * - protector: a pointer to the zero-copy protector returned from the method.
+ *
+ * This method returns TSI_OK on success or a specific error code otherwise.
+ */
+tsi_result alts_zero_copy_grpc_protector_create(
+ const uint8_t* key, size_t key_size, bool is_rekey, bool is_client,
+ bool is_integrity_only, size_t* max_protected_frame_size,
+ tsi_zero_copy_grpc_protector** protector);
+
+#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_ZERO_COPY_GRPC_PROTECTOR_H \
+ */