aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/tsi/transport_security_interface.h
blob: 6be72c753a79039cdcdc9a3438b6c8a5eeec6229 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
/*
 *
 * Copyright 2014, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#ifndef __TRANSPORT_SECURITY_INTERFACE_H_
#define __TRANSPORT_SECURITY_INTERFACE_H_

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/* --- 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_result;

const char* tsi_result_to_string(tsi_result result);


/* --- 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];
   uint32_t protected_buffer_size = sizeof(protected_buffer);
   tsi_result result = TSI_OK;
   while (message_size > 0) {
     uint32_t protected_buffer_size_to_send = protected_buffer_size;
     uint32_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) {
       uint32_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,
    uint32_t* unprotected_bytes_size,
    unsigned char* protected_output_frames,
    uint32_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,
    uint32_t* protected_output_frames_size,
    uint32_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.

   - 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,
    uint32_t* protected_frames_bytes_size,
    unsigned char* unprotected_bytes,
    uint32_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"

/* This property is of type TSI_PEER_PROPERTY_STRING.  */
#define TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY "x509_subject_common_name"

/* This property is of type TSI_PEER_PROPERTY_LIST and the children contain
   unnamed (name == NULL) properties of type TSI_PEER_PROPERTY_STRING.  */
#define TSI_X509_SUBJECT_ALTERNATIVE_NAMES_PEER_PROPERTY \
  "x509_subject_alternative_names"

/* This property is of type TSI_PEER_PROPERTY_STRING. */
#define TSI_SSL_ALPN_SELECTED_PROTOCOL "ssl_alpn_selected_protocol"

/* This property is of type TSI_PEER_PROPERTY_STRING.  */
#define TSI_MDB_USER_NAME_PEER_PROPERTY "mdb_user_name"

/* This property is of type TSI_PEER_PROPERTY_SIGNED_INTEGER.  */
#define TSI_MDB_GAIA_ID_PEER_PROPERTY "mdb_gaia_id"

/* Properties of type TSI_PEER_PROPERTY_TYPE_STRING may contain NULL characters
   just like C++ strings. The length field gives the length of the string.  */
typedef enum {
  TSI_PEER_PROPERTY_TYPE_SIGNED_INTEGER,
  TSI_PEER_PROPERTY_TYPE_UNSIGNED_INTEGER,
  TSI_PEER_PROPERTY_TYPE_REAL,
  TSI_PEER_PROPERTY_TYPE_STRING,
  TSI_PEER_PROPERTY_TYPE_LIST
} tsi_peer_property_type;

/* The relevant field in the union value is dictated by the type field.
   name may be NULL in case of an unnamed property. */
typedef struct tsi_peer_property {
  char* name;
  tsi_peer_property_type type;
  union {
    int64_t signed_int;
    uint64_t unsigned_int;
    double real;
    struct {
      char* data;
      uint32_t length;
    } string;
    struct {
      struct tsi_peer_property* children;
      uint32_t child_count;
    } list;
  } value;
} tsi_peer_property;

typedef struct {
  tsi_peer_property* properties;
  uint32_t property_count;
} tsi_peer;

/* Gets the first property with the specified name. Iteration over the
   properties of the peer should be used if the client of the API is expecting
   several properties with the same name.
   Returns NULL if there is no corresponding property.  */
const tsi_peer_property* tsi_peer_get_property_by_name(const tsi_peer* self,
                                                       const char* name);

/* Destructs the tsi_peer object. */
void tsi_peer_destruct(tsi_peer* self);

/* --- tsi_handshaker objects ----

   Implementations of this object must be thread compatible.

   A typical usage of this object would be:

   ------------------------------------------------------------------------
   tsi_result result = TSI_OK;
   unsigned char buf[4096];
   uint32_t buf_offset;
   uint32_t buf_size;
   while (1) {
     // See if we need to send some bytes to the peer.
     do {
       uint32_t buf_size_to_send = sizeof(buf);
       result = tsi_handshaker_get_bytes_to_send_to_peer(handshaker, buf,
                                                         &buf_size_to_send);
       if (buf_size_to_send > 0) send_bytes_to_peer(buf, buf_size_to_send);
     } while (result == TSI_INCOMPLETE_DATA);
     if (result != TSI_OK) return result;
     if (!tsi_handshaker_is_in_progress(handshaker)) break;

     do {
       // Read bytes from the peer.
       buf_size = sizeof(buf);
       buf_offset = 0;
       read_bytes_from_peer(buf, &buf_size);
       if (buf_size == 0) break;

       // Process the bytes from the peer. We have to be careful as these bytes
       // may contain non-handshake data (protected data). If this is the case,
       // we will exit from the loop with buf_size > 0.
       uint32_t consumed_by_handshaker = buf_size;
       result = tsi_handshaker_process_bytes_from_peer(
           handshaker, buf, &consumed_by_handshaker);
       buf_size -= consumed_by_handshaker;
       buf_offset += consumed_by_handshaker;
     } while (result == TSI_INCOMPLETE_DATA);

     if (result != TSI_OK) return result;
     if (!tsi_handshaker_is_in_progress(handshaker)) break;
   }

   // Check the Peer.
   tsi_peer peer;
   do {
     result = tsi_handshaker_extract_peer(handshaker, &peer);
     if (result != TSI_OK) break;
     result = check_peer(&peer);
   } while (0);
   tsi_peer_destruct(&peer);
   if (result != TSI_OK) return result;

   // Create the protector.
   tsi_frame_protector* protector = NULL;
   result = tsi_handshaker_create_frame_protector(handshaker, NULL,
                                                  &protector);
   if (result != TSI_OK) return result;

   // Do not forget to unprotect outstanding data if any.
   if (buf_size > 0) {
     result = tsi_frame_protector_unprotect(protector, buf + buf_offset,
                                            buf_size, ..., ...);
     ....
   }
   ...
   ------------------------------------------------------------------------   */
typedef struct tsi_handshaker tsi_handshaker;

/* 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,
                                                    uint32_t* bytes_size);

/* 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,
                                                  uint32_t* bytes_size);

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

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


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

/* 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,
    uint32_t* max_output_protected_frame_size,
    tsi_frame_protector** protector);

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

#ifdef __cplusplus
}
#endif

#endif  /* __TRANSPORT_SECURITY_INTERFACE_H_ */