3 * Copyright 2018 gRPC authors.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
19 #include <grpc/support/port_platform.h>
21 #include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h"
23 #include <grpc/support/alloc.h>
24 #include <grpc/support/log.h>
28 #include "src/core/lib/slice/slice_internal.h"
29 #include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h"
30 #include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h"
32 /* Main struct for alts_grpc_integrity_only_record_protocol. */
33 typedef struct alts_grpc_integrity_only_record_protocol {
34 alts_grpc_record_protocol base;
35 bool enable_extra_copy;
36 grpc_slice_buffer data_sb;
37 unsigned char* tag_buf;
38 } alts_grpc_integrity_only_record_protocol;
40 /* --- alts_grpc_record_protocol methods implementation. --- */
42 static tsi_result alts_grpc_integrity_only_extra_copy_protect(
43 alts_grpc_record_protocol* rp, grpc_slice_buffer* unprotected_slices,
44 grpc_slice_buffer* protected_slices) {
45 /* Allocates memory for protected frame and copies data. */
46 size_t data_length = unprotected_slices->length;
47 size_t protected_frame_size =
48 unprotected_slices->length + rp->header_length + rp->tag_length;
49 grpc_slice protected_slice = GRPC_SLICE_MALLOC(protected_frame_size);
50 uint8_t* data = GRPC_SLICE_START_PTR(protected_slice) + rp->header_length;
51 for (size_t i = 0; i < unprotected_slices->count; i++) {
52 memcpy(data, GRPC_SLICE_START_PTR(unprotected_slices->slices[i]),
53 GRPC_SLICE_LENGTH(unprotected_slices->slices[i]));
54 data += GRPC_SLICE_LENGTH(unprotected_slices->slices[i]);
56 /* Calls alts_iovec_record_protocol protect. */
57 char* error_details = nullptr;
58 iovec_t header_iovec = {GRPC_SLICE_START_PTR(protected_slice),
61 GRPC_SLICE_START_PTR(protected_slice) + rp->header_length + data_length,
63 rp->iovec_buf[0].iov_base =
64 GRPC_SLICE_START_PTR(protected_slice) + rp->header_length;
65 rp->iovec_buf[0].iov_len = data_length;
66 grpc_status_code status = alts_iovec_record_protocol_integrity_only_protect(
67 rp->iovec_rp, rp->iovec_buf, 1, header_iovec, tag_iovec, &error_details);
68 if (status != GRPC_STATUS_OK) {
69 gpr_log(GPR_ERROR, "Failed to protect, %s", error_details);
70 gpr_free(error_details);
71 return TSI_INTERNAL_ERROR;
73 grpc_slice_buffer_add(protected_slices, protected_slice);
74 grpc_slice_buffer_reset_and_unref_internal(unprotected_slices);
78 static tsi_result alts_grpc_integrity_only_protect(
79 alts_grpc_record_protocol* rp, grpc_slice_buffer* unprotected_slices,
80 grpc_slice_buffer* protected_slices) {
81 /* Input sanity check. */
82 if (rp == nullptr || unprotected_slices == nullptr ||
83 protected_slices == nullptr) {
85 "Invalid nullptr arguments to alts_grpc_record_protocol protect.");
86 return TSI_INVALID_ARGUMENT;
88 alts_grpc_integrity_only_record_protocol* integrity_only_record_protocol =
89 reinterpret_cast<alts_grpc_integrity_only_record_protocol*>(rp);
90 if (integrity_only_record_protocol->enable_extra_copy) {
91 return alts_grpc_integrity_only_extra_copy_protect(rp, unprotected_slices,
94 /* Allocates memory for header and tag slices. */
95 grpc_slice header_slice = GRPC_SLICE_MALLOC(rp->header_length);
96 grpc_slice tag_slice = GRPC_SLICE_MALLOC(rp->tag_length);
97 /* Calls alts_iovec_record_protocol protect. */
98 char* error_details = nullptr;
99 iovec_t header_iovec = {GRPC_SLICE_START_PTR(header_slice),
100 GRPC_SLICE_LENGTH(header_slice)};
101 iovec_t tag_iovec = {GRPC_SLICE_START_PTR(tag_slice),
102 GRPC_SLICE_LENGTH(tag_slice)};
103 alts_grpc_record_protocol_convert_slice_buffer_to_iovec(rp,
105 grpc_status_code status = alts_iovec_record_protocol_integrity_only_protect(
106 rp->iovec_rp, rp->iovec_buf, unprotected_slices->count, header_iovec,
107 tag_iovec, &error_details);
108 if (status != GRPC_STATUS_OK) {
109 gpr_log(GPR_ERROR, "Failed to protect, %s", error_details);
110 gpr_free(error_details);
111 return TSI_INTERNAL_ERROR;
113 /* Appends result to protected_slices. */
114 grpc_slice_buffer_add(protected_slices, header_slice);
115 grpc_slice_buffer_move_into(unprotected_slices, protected_slices);
116 grpc_slice_buffer_add(protected_slices, tag_slice);
120 static tsi_result alts_grpc_integrity_only_unprotect(
121 alts_grpc_record_protocol* rp, grpc_slice_buffer* protected_slices,
122 grpc_slice_buffer* unprotected_slices) {
123 /* Input sanity check. */
124 if (rp == nullptr || protected_slices == nullptr ||
125 unprotected_slices == nullptr) {
128 "Invalid nullptr arguments to alts_grpc_record_protocol unprotect.");
129 return TSI_INVALID_ARGUMENT;
131 if (protected_slices->length < rp->header_length + rp->tag_length) {
132 gpr_log(GPR_ERROR, "Protected slices do not have sufficient data.");
133 return TSI_INVALID_ARGUMENT;
135 /* In this method, rp points to alts_grpc_record_protocol struct
136 * and integrity_only_record_protocol points to
137 * alts_grpc_integrity_only_record_protocol struct. */
138 alts_grpc_integrity_only_record_protocol* integrity_only_record_protocol =
139 reinterpret_cast<alts_grpc_integrity_only_record_protocol*>(rp);
140 /* Strips frame header from protected slices. */
141 grpc_slice_buffer_reset_and_unref_internal(&rp->header_sb);
142 grpc_slice_buffer_move_first(protected_slices, rp->header_length,
144 GPR_ASSERT(rp->header_sb.length == rp->header_length);
145 iovec_t header_iovec = alts_grpc_record_protocol_get_header_iovec(rp);
146 /* Moves protected slices data to data_sb and leaves the remaining tag. */
147 grpc_slice_buffer_reset_and_unref_internal(
148 &integrity_only_record_protocol->data_sb);
149 grpc_slice_buffer_move_first(protected_slices,
150 protected_slices->length - rp->tag_length,
151 &integrity_only_record_protocol->data_sb);
152 GPR_ASSERT(protected_slices->length == rp->tag_length);
153 iovec_t tag_iovec = {nullptr, rp->tag_length};
154 if (protected_slices->count == 1) {
155 tag_iovec.iov_base = GRPC_SLICE_START_PTR(protected_slices->slices[0]);
157 /* Frame tag is in multiple slices, copies the tag bytes from slice
158 * buffer to a single flat buffer. */
159 alts_grpc_record_protocol_copy_slice_buffer(
160 protected_slices, integrity_only_record_protocol->tag_buf);
161 tag_iovec.iov_base = integrity_only_record_protocol->tag_buf;
163 /* Calls alts_iovec_record_protocol unprotect. */
164 char* error_details = nullptr;
165 alts_grpc_record_protocol_convert_slice_buffer_to_iovec(
166 rp, &integrity_only_record_protocol->data_sb);
167 grpc_status_code status = alts_iovec_record_protocol_integrity_only_unprotect(
168 rp->iovec_rp, rp->iovec_buf,
169 integrity_only_record_protocol->data_sb.count, header_iovec, tag_iovec,
171 if (status != GRPC_STATUS_OK) {
172 gpr_log(GPR_ERROR, "Failed to unprotect, %s", error_details);
173 gpr_free(error_details);
174 return TSI_INTERNAL_ERROR;
176 grpc_slice_buffer_reset_and_unref_internal(&rp->header_sb);
177 grpc_slice_buffer_reset_and_unref_internal(protected_slices);
178 grpc_slice_buffer_move_into(&integrity_only_record_protocol->data_sb,
183 static void alts_grpc_integrity_only_destruct(alts_grpc_record_protocol* rp) {
187 alts_grpc_integrity_only_record_protocol* integrity_only_rp =
188 reinterpret_cast<alts_grpc_integrity_only_record_protocol*>(rp);
189 grpc_slice_buffer_destroy_internal(&integrity_only_rp->data_sb);
190 gpr_free(integrity_only_rp->tag_buf);
193 static const alts_grpc_record_protocol_vtable
194 alts_grpc_integrity_only_record_protocol_vtable = {
195 alts_grpc_integrity_only_protect, alts_grpc_integrity_only_unprotect,
196 alts_grpc_integrity_only_destruct};
198 tsi_result alts_grpc_integrity_only_record_protocol_create(
199 gsec_aead_crypter* crypter, size_t overflow_size, bool is_client,
200 bool is_protect, bool enable_extra_copy, alts_grpc_record_protocol** rp) {
201 if (crypter == nullptr || rp == nullptr) {
203 "Invalid nullptr arguments to alts_grpc_record_protocol create.");
204 return TSI_INVALID_ARGUMENT;
206 alts_grpc_integrity_only_record_protocol* impl =
207 static_cast<alts_grpc_integrity_only_record_protocol*>(
208 gpr_zalloc(sizeof(alts_grpc_integrity_only_record_protocol)));
209 /* Calls alts_grpc_record_protocol init. */
210 tsi_result result = alts_grpc_record_protocol_init(
211 &impl->base, crypter, overflow_size, is_client,
212 /*is_integrity_only=*/true, is_protect);
213 if (result != TSI_OK) {
217 impl->enable_extra_copy = enable_extra_copy;
218 /* Initializes slice buffer for data_sb. */
219 grpc_slice_buffer_init(&impl->data_sb);
220 /* Allocates tag buffer. */
222 static_cast<unsigned char*>(gpr_malloc(impl->base.tag_length));
223 impl->base.vtable = &alts_grpc_integrity_only_record_protocol_vtable;