--- /dev/null
+/*
+ *
+ * Copyright 2015 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_TRANSPORT_SECURITY_INTERFACE_H
+#define GRPC_CORE_TSI_TRANSPORT_SECURITY_INTERFACE_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "src/core/lib/debug/trace.h"
+
+/* --- tsi result --- */
+
+typedef enum {
+ TSI_OK = 0,
+ TSI_UNKNOWN_ERROR = 1,
+ TSI_INVALID_ARGUMENT = 2,
+ TSI_PERMISSION_DENIED = 3,
+ TSI_INCOMPLETE_DATA = 4,
+ TSI_FAILED_PRECONDITION = 5,
+ TSI_UNIMPLEMENTED = 6,
+ TSI_INTERNAL_ERROR = 7,
+ TSI_DATA_CORRUPTED = 8,
+ TSI_NOT_FOUND = 9,
+ TSI_PROTOCOL_FAILURE = 10,
+ TSI_HANDSHAKE_IN_PROGRESS = 11,
+ TSI_OUT_OF_RESOURCES = 12,
+ TSI_ASYNC = 13,
+ TSI_HANDSHAKE_SHUTDOWN = 14,
+} tsi_result;
+
+typedef enum {
+ // Default option
+ TSI_DONT_REQUEST_CLIENT_CERTIFICATE,
+ TSI_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY,
+ TSI_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY,
+ TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY,
+ TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY,
+} tsi_client_certificate_request_type;
+
+const char* tsi_result_to_string(tsi_result result);
+
+/* --- tsi tracing --- */
+
+extern grpc_core::TraceFlag tsi_tracing_enabled;
+
+/* -- tsi_zero_copy_grpc_protector object --
+
+ This object protects and unprotects grpc slice buffers with zero or minimized
+ memory copy once the handshake is done. Implementations of this object must be
+ thread compatible. This object depends on grpc and the details of this object
+ is defined in transport_security_grpc.h. */
+
+typedef struct tsi_zero_copy_grpc_protector tsi_zero_copy_grpc_protector;
+
+/* --- tsi_frame_protector object ---
+
+ This object protects and unprotects buffers once the handshake is done.
+ Implementations of this object must be thread compatible. */
+
+typedef struct tsi_frame_protector tsi_frame_protector;
+
+/* Outputs protected frames.
+ - unprotected_bytes is an input only parameter and points to the data
+ to be protected.
+ - unprotected_bytes_size is an input/output parameter used by the caller to
+ specify how many bytes are available in unprotected_bytes. The output
+ value is the number of bytes consumed during the call.
+ - protected_output_frames points to a buffer allocated by the caller that
+ will be written.
+ - protected_output_frames_size is an input/output parameter used by the
+ caller to specify how many bytes are available in protected_output_frames.
+ As an output, this value indicates the number of bytes written.
+ - This method returns TSI_OK in case of success or a specific error code in
+ case of failure. Note that even if all the input unprotected bytes are
+ consumed, they may not have been processed into the returned protected
+ output frames. The caller should call the protect_flush method
+ to make sure that there are no more protected bytes buffered in the
+ protector.
+
+ A typical way to call this method would be:
+
+ ------------------------------------------------------------------------
+ unsigned char protected_buffer[4096];
+ size_t protected_buffer_size = sizeof(protected_buffer);
+ tsi_result result = TSI_OK;
+ while (message_size > 0) {
+ size_t protected_buffer_size_to_send = protected_buffer_size;
+ size_t processed_message_size = message_size;
+ result = tsi_frame_protector_protect(protector,
+ message_bytes,
+ &processed_message_size,
+ protected_buffer,
+ &protected_buffer_size_to_send);
+ if (result != TSI_OK) break;
+ send_bytes_to_peer(protected_buffer, protected_buffer_size_to_send);
+ message_bytes += processed_message_size;
+ message_size -= processed_message_size;
+
+ // Don't forget to flush.
+ if (message_size == 0) {
+ size_t still_pending_size;
+ do {
+ protected_buffer_size_to_send = protected_buffer_size;
+ result = tsi_frame_protector_protect_flush(
+ protector, protected_buffer,
+ &protected_buffer_size_to_send, &still_pending_size);
+ if (result != TSI_OK) break;
+ send_bytes_to_peer(protected_buffer, protected_buffer_size_to_send);
+ } while (still_pending_size > 0);
+ }
+ }
+
+ if (result != TSI_OK) HandleError(result);
+ ------------------------------------------------------------------------ */
+tsi_result tsi_frame_protector_protect(tsi_frame_protector* self,
+ const unsigned char* unprotected_bytes,
+ size_t* unprotected_bytes_size,
+ unsigned char* protected_output_frames,
+ size_t* protected_output_frames_size);
+
+/* Indicates that we need to flush the bytes buffered in the protector and get
+ the resulting frame.
+ - protected_output_frames points to a buffer allocated by the caller that
+ will be written.
+ - protected_output_frames_size is an input/output parameter used by the
+ caller to specify how many bytes are available in protected_output_frames.
+ - still_pending_bytes is an output parameter indicating the number of bytes
+ that still need to be flushed from the protector.*/
+tsi_result tsi_frame_protector_protect_flush(
+ tsi_frame_protector* self, unsigned char* protected_output_frames,
+ size_t* protected_output_frames_size, size_t* still_pending_size);
+
+/* Outputs unprotected bytes.
+ - protected_frames_bytes is an input only parameter and points to the
+ protected frames to be unprotected.
+ - protected_frames_bytes_size is an input/output only parameter used by the
+ caller to specify how many bytes are available in protected_bytes. The
+ output value is the number of bytes consumed during the call.
+ Implementations will buffer up to a frame of protected data.
+ - unprotected_bytes points to a buffer allocated by the caller that will be
+ written.
+ - unprotected_bytes_size is an input/output parameter used by the caller to
+ specify how many bytes are available in unprotected_bytes. This
+ value is expected to be at most max_protected_frame_size minus overhead
+ which means that max_protected_frame_size is a safe bet. The output value
+ is the number of bytes actually written.
+ If *unprotected_bytes_size is unchanged, there may be more data remaining
+ to unprotect, and the caller should call this function again.
+
+ - This method returns TSI_OK in case of success. Success includes cases where
+ there is not enough data to output a frame in which case
+ unprotected_bytes_size will be set to 0 and cases where the internal buffer
+ needs to be read before new protected data can be processed in which case
+ protected_frames_size will be set to 0. */
+tsi_result tsi_frame_protector_unprotect(
+ tsi_frame_protector* self, const unsigned char* protected_frames_bytes,
+ size_t* protected_frames_bytes_size, unsigned char* unprotected_bytes,
+ size_t* unprotected_bytes_size);
+
+/* Destroys the tsi_frame_protector object. */
+void tsi_frame_protector_destroy(tsi_frame_protector* self);
+
+/* --- tsi_peer objects ---
+
+ tsi_peer objects are a set of properties. The peer owns the properties. */
+
+/* This property is of type TSI_PEER_PROPERTY_STRING. */
+#define TSI_CERTIFICATE_TYPE_PEER_PROPERTY "certificate_type"
+
+/* Property values may contain NULL characters just like C++ strings.
+ The length field gives the length of the string. */
+typedef struct tsi_peer_property {
+ char* name;
+ struct {
+ char* data;
+ size_t length;
+ } value;
+} tsi_peer_property;
+
+typedef struct {
+ tsi_peer_property* properties;
+ size_t property_count;
+} tsi_peer;
+
+/* Destructs the tsi_peer object. */
+void tsi_peer_destruct(tsi_peer* self);
+
+/* --- tsi_handshaker_result object ---
+
+ This object contains all necessary handshake results and data such as peer
+ info, negotiated keys, unused handshake bytes, when the handshake completes.
+ Implementations of this object must be thread compatible. */
+
+typedef struct tsi_handshaker_result tsi_handshaker_result;
+
+/* This method extracts tsi peer. It returns TSI_OK assuming there is no fatal
+ error.
+ The caller is responsible for destructing the peer. */
+tsi_result tsi_handshaker_result_extract_peer(const tsi_handshaker_result* self,
+ tsi_peer* peer);
+
+/* This method creates a tsi_frame_protector object. It returns TSI_OK assuming
+ there is no fatal error.
+ The caller is responsible for destroying the protector. */
+tsi_result tsi_handshaker_result_create_frame_protector(
+ const tsi_handshaker_result* self, size_t* max_output_protected_frame_size,
+ tsi_frame_protector** protector);
+
+/* This method returns the unused bytes from the handshake. It returns TSI_OK
+ assuming there is no fatal error.
+ Ownership of the bytes is retained by the handshaker result. As a
+ consequence, the caller must not free the bytes. */
+tsi_result tsi_handshaker_result_get_unused_bytes(
+ const tsi_handshaker_result* self, const unsigned char** bytes,
+ size_t* byte_size);
+
+/* This method releases the tsi_handshaker_handshaker object. After this method
+ is called, no other method can be called on the object. */
+void tsi_handshaker_result_destroy(tsi_handshaker_result* self);
+
+/* --- tsi_handshaker objects ----
+
+ Implementations of this object must be thread compatible.
+
+ ------------------------------------------------------------------------
+
+ A typical usage supporting both synchronous and asynchronous TSI handshaker
+ implementations would be:
+
+ ------------------------------------------------------------------------
+
+ typedef struct {
+ tsi_handshaker *handshaker;
+ tsi_handshaker_result *handshaker_result;
+ unsigned char *handshake_buffer;
+ size_t handshake_buffer_size;
+ ...
+ } security_handshaker;
+
+ void do_handshake(security_handshaker *h, ...) {
+ // Start the handshake by the calling do_handshake_next.
+ do_handshake_next(h, NULL, 0);
+ ...
+ }
+
+ // This method is the callback function when data is received from the
+ // peer. This method will read bytes into the handshake buffer and call
+ // do_handshake_next.
+ void on_handshake_data_received_from_peer(void *user_data) {
+ security_handshaker *h = (security_handshaker *)user_data;
+ size_t bytes_received_size = h->handshake_buffer_size;
+ read_bytes_from_peer(h->handshake_buffer, &bytes_received_size);
+ do_handshake_next(h, h->handshake_buffer, bytes_received_size);
+ }
+
+ // This method processes a step of handshake, calling tsi_handshaker_next.
+ void do_handshake_next(security_handshaker *h,
+ const unsigned char* bytes_received,
+ size_t bytes_received_size) {
+ tsi_result status = TSI_OK;
+ unsigned char *bytes_to_send = NULL;
+ size_t bytes_to_send_size = 0;
+ tsi_handshaker_result *result = NULL;
+ status = tsi_handshaker_next(
+ handshaker, bytes_received, bytes_received_size, &bytes_to_send,
+ &bytes_to_send_size, &result, on_handshake_next_done, h);
+ // If TSI handshaker is asynchronous, on_handshake_next_done will be
+ // executed inside tsi_handshaker_next.
+ if (status == TSI_ASYNC) return;
+ // If TSI handshaker is synchronous, invoke callback directly in this
+ // thread.
+ on_handshake_next_done(status, (void *)h, bytes_to_send,
+ bytes_to_send_size, result);
+ }
+
+ // This is the callback function to execute after tsi_handshaker_next.
+ // It is passed to tsi_handshaker_next as a function parameter.
+ void on_handshake_next_done(
+ tsi_result status, void *user_data, const unsigned char *bytes_to_send,
+ size_t bytes_to_send_size, tsi_handshaker_result *result) {
+ security_handshaker *h = (security_handshaker *)user_data;
+ if (status == TSI_INCOMPLETE_DATA) {
+ // Schedule an asynchronous read from the peer. If handshake data are
+ // received, on_handshake_data_received_from_peer will be called.
+ async_read_from_peer(..., ..., on_handshake_data_received_from_peer);
+ return;
+ }
+ if (status != TSI_OK) return;
+
+ if (bytes_to_send_size > 0) {
+ send_bytes_to_peer(bytes_to_send, bytes_to_send_size);
+ }
+
+ if (result != NULL) {
+ // Handshake completed.
+ h->result = result;
+ // Check the Peer.
+ tsi_peer peer;
+ status = tsi_handshaker_result_extract_peer(result, &peer);
+ if (status != TSI_OK) return;
+ status = check_peer(&peer);
+ tsi_peer_destruct(&peer);
+ if (status != TSI_OK) return;
+
+ // Create the protector.
+ tsi_frame_protector* protector = NULL;
+ status = tsi_handshaker_result_create_frame_protector(result, NULL,
+ &protector);
+ if (status != TSI_OK) return;
+
+ // Do not forget to unprotect outstanding data if any.
+ ....
+ }
+ }
+ ------------------------------------------------------------------------ */
+typedef struct tsi_handshaker tsi_handshaker;
+
+/* TODO(jiangtaoli2016): Cleans up deprecated methods when we are ready. */
+
+/* TO BE DEPRECATED SOON. Use tsi_handshaker_next instead.
+ Gets bytes that need to be sent to the peer.
+ - bytes is the buffer that will be written with the data to be sent to the
+ peer.
+ - bytes_size is an input/output parameter specifying the capacity of the
+ bytes parameter as input and the number of bytes written as output.
+ Returns TSI_OK if all the data to send to the peer has been written or if
+ nothing has to be sent to the peer (in which base bytes_size outputs to 0),
+ otherwise returns TSI_INCOMPLETE_DATA which indicates that this method
+ needs to be called again to get all the bytes to send to the peer (there
+ was more data to write than the specified bytes_size). In case of a fatal
+ error in the handshake, another specific error code is returned. */
+tsi_result tsi_handshaker_get_bytes_to_send_to_peer(tsi_handshaker* self,
+ unsigned char* bytes,
+ size_t* bytes_size);
+
+/* TO BE DEPRECATED SOON. Use tsi_handshaker_next instead.
+ Processes bytes received from the peer.
+ - bytes is the buffer containing the data.
+ - bytes_size is an input/output parameter specifying the size of the data as
+ input and the number of bytes consumed as output.
+ Return TSI_OK if the handshake has all the data it needs to process,
+ otherwise return TSI_INCOMPLETE_DATA which indicates that this method
+ needs to be called again to complete the data needed for processing. In
+ case of a fatal error in the handshake, another specific error code is
+ returned. */
+tsi_result tsi_handshaker_process_bytes_from_peer(tsi_handshaker* self,
+ const unsigned char* bytes,
+ size_t* bytes_size);
+
+/* TO BE DEPRECATED SOON.
+ Gets the result of the handshaker.
+ Returns TSI_OK if the hanshake completed successfully and there has been no
+ errors. Returns TSI_HANDSHAKE_IN_PROGRESS if the handshaker is not done yet
+ but no error has been encountered so far. Otherwise the handshaker failed
+ with the returned error. */
+tsi_result tsi_handshaker_get_result(tsi_handshaker* self);
+
+/* TO BE DEPRECATED SOON.
+ Returns 1 if the handshake is in progress, 0 otherwise. */
+#define tsi_handshaker_is_in_progress(h) \
+ (tsi_handshaker_get_result((h)) == TSI_HANDSHAKE_IN_PROGRESS)
+
+/* TO BE DEPRECATED SOON. Use tsi_handshaker_result_extract_peer instead.
+ This method may return TSI_FAILED_PRECONDITION if
+ tsi_handshaker_is_in_progress returns 1, it returns TSI_OK otherwise
+ assuming the handshaker is not in a fatal error state.
+ The caller is responsible for destructing the peer. */
+tsi_result tsi_handshaker_extract_peer(tsi_handshaker* self, tsi_peer* peer);
+
+/* TO BE DEPRECATED SOON. Use tsi_handshaker_result_create_frame_protector
+ instead.
+ This method creates a tsi_frame_protector object after the handshake phase
+ is done. After this method has been called successfully, the only method
+ that can be called on this object is Destroy.
+ - max_output_protected_frame_size is an input/output parameter specifying the
+ desired max output protected frame size as input and outputing the actual
+ max output frame size as the output. Passing NULL is OK and will result in
+ the implementation choosing the default maximum protected frame size. Note
+ that this size only applies to outgoing frames (generated with
+ tsi_frame_protector_protect) and not incoming frames (input of
+ tsi_frame_protector_unprotect).
+ - protector is an output parameter pointing to the newly created
+ tsi_frame_protector object.
+ This method may return TSI_FAILED_PRECONDITION if
+ tsi_handshaker_is_in_progress returns 1, it returns TSI_OK otherwise assuming
+ the handshaker is not in a fatal error state.
+ The caller is responsible for destroying the protector. */
+tsi_result tsi_handshaker_create_frame_protector(
+ tsi_handshaker* self, size_t* max_output_protected_frame_size,
+ tsi_frame_protector** protector);
+
+/* Callback function definition for tsi_handshaker_next.
+ - status indicates the status of the next operation.
+ - user_data is the argument to callback function passed from the caller.
+ - bytes_to_send is the data buffer to be sent to the peer.
+ - bytes_to_send_size is the size of data buffer to be sent to the peer.
+ - handshaker_result is the result of handshake when the handshake completes,
+ is NULL otherwise. */
+typedef void (*tsi_handshaker_on_next_done_cb)(
+ tsi_result status, void* user_data, const unsigned char* bytes_to_send,
+ size_t bytes_to_send_size, tsi_handshaker_result* handshaker_result);
+
+/* Conduct a next step of the handshake.
+ - received_bytes is the buffer containing the data received from the peer.
+ - received_bytes_size is the size of the data received from the peer.
+ - bytes_to_send is the data buffer to be sent to the peer.
+ - bytes_to_send_size is the size of data buffer to be sent to the peer.
+ - handshaker_result is the result of handshake if the handshake completes.
+ - cb is the callback function defined above. It can be NULL for synchronous
+ TSI handshaker implementation.
+ - user_data is the argument to callback function passed from the caller.
+ This method returns TSI_ASYNC if the TSI handshaker implementation is
+ asynchronous, and in this case, the callback is guaranteed to run in another
+ thread owned by TSI. It returns TSI_OK if the handshake completes or if
+ there are data to send to the peer, otherwise returns TSI_INCOMPLETE_DATA
+ which indicates that this method needs to be called again with more data
+ from the peer. In case of a fatal error in the handshake, another specific
+ error code is returned.
+ The caller is responsible for destroying the handshaker_result. However,
+ the caller should not free bytes_to_send, as the buffer is owned by the
+ tsi_handshaker object. */
+tsi_result tsi_handshaker_next(
+ tsi_handshaker* self, const unsigned char* received_bytes,
+ size_t received_bytes_size, const unsigned char** bytes_to_send,
+ size_t* bytes_to_send_size, tsi_handshaker_result** handshaker_result,
+ tsi_handshaker_on_next_done_cb cb, void* user_data);
+
+/* This method shuts down a TSI handshake that is in progress.
+ *
+ * This method will be invoked when TSI handshake should be terminated before
+ * being finished in order to free any resources being used.
+ */
+void tsi_handshaker_shutdown(tsi_handshaker* self);
+
+/* This method releases the tsi_handshaker object. After this method is called,
+ no other method can be called on the object. */
+void tsi_handshaker_destroy(tsi_handshaker* self);
+
+/* This method initializes the necessary shared objects used for tsi
+ implementation. */
+void tsi_init();
+
+/* This method destroys the shared objects created by tsi_init. */
+void tsi_destroy();
+
+#endif /* GRPC_CORE_TSI_TRANSPORT_SECURITY_INTERFACE_H */