blob: 8a9bb6de339b5054ba2ece1e519a986b859860c3 [file] [log] [blame]
Laurens Van Houtven7d2c74e2014-06-23 13:49:58 +02001C bindings
2==========
3
4C bindings are bindings to C libraries, using cffi_ whenever possible.
5
Alex Gaynor988df9b2016-04-28 10:57:16 -04006.. _cffi: https://cffi.readthedocs.io
Laurens Van Houtven7d2c74e2014-06-23 13:49:58 +02007
8Bindings live in :py:mod:`cryptography.hazmat.bindings`.
Laurens Van Houtvenefa5cfb2014-06-23 13:51:35 +02009
Paul Kehrer836955f2014-11-29 19:10:47 -100010When modifying the bindings you will need to recompile the C extensions to
11test the changes. This can be accomplished with ``pip install -e .`` in the
12project root. If you do not do this a ``RuntimeError`` will be raised.
13
Laurens Van Houtven220a98d2014-06-23 14:08:27 +020014Style guide
15-----------
16
17Don't name parameters:
18
19.. code-block:: c
20
21 /* Good */
22 long f(long);
23 /* Bad */
24 long f(long x);
25
26...unless they're inside a struct:
27
28.. code-block:: c
29
30 struct my_struct {
31 char *name;
32 int number;
33 ...;
34 };
35
36Include ``void`` if the function takes no arguments:
37
38.. code-block:: c
39
40 /* Good */
41 long f(void);
42 /* Bad */
43 long f();
44
45Wrap lines at 80 characters like so:
46
47.. code-block:: c
48
49 /* Pretend this went to 80 characters */
50 long f(long, long,
51 int *)
52
53Include a space after commas between parameters:
54
55.. code-block:: c
56
57 /* Good */
58 long f(int, char *)
59 /* Bad */
60 long f(int,char *)
61
62Use C-style ``/* */`` comments instead of C++-style ``//``:
63
64.. code-block:: c
65
66 // Bad
67 /* Good */
68
69Values set by ``#define`` should be assigned the appropriate type. If you see
70this:
71
72.. code-block:: c
73
74 #define SOME_INTEGER_LITERAL 0x0;
75 #define SOME_UNSIGNED_INTEGER_LITERAL 0x0001U;
76 #define SOME_STRING_LITERAL "hello";
77
78...it should be added to the bindings like so:
79
80.. code-block:: c
81
82 static const int SOME_INTEGER_LITERAL;
83 static const unsigned int SOME_UNSIGNED_INTEGER_LITERAL;
84 static const char *const SOME_STRING_LITERAL;
85
Laurens Van Houtvenefa5cfb2014-06-23 13:51:35 +020086Adding constant, types, functions...
87------------------------------------
88
89You can create bindings for any name that exists in some version of
90the library you're binding against. However, the project also has to
Alex Gaynor89c2ce42017-04-26 10:44:14 -040091keep supporting older versions of the library. In order to achieve this,
92binding modules have a ``CUSTOMIZATIONS`` constant, and there is a
93``CONDITIONAL_NAMES`` constants in
94``src/cryptography/hazmat/bindings/openssl/_conditional.py``.
Laurens Van Houtvenefa5cfb2014-06-23 13:51:35 +020095
96Let's say you want to enable quantum transmogrification. The upstream
97library implements this as the following API::
98
99 static const int QM_TRANSMOGRIFICATION_ALIGNMENT_LEFT;
100 static const int QM_TRANSMOGRIFICATION_ALIGNMENT_RIGHT;
101 typedef ... QM_TRANSMOGRIFICATION_CTX;
102 int QM_transmogrify(QM_TRANSMOGRIFICATION_CTX *, int);
103
104To start, create a new constant that defines if the *actual* library
105has the feature you want, and add it to ``TYPES``::
106
107 static const long Cryptography_HAS_QUANTUM_TRANSMOGRIFICATION;
108
109This should start with ``Cryptography_``, since we're adding it in
110this library. This prevents namespace collisions.
111
112Then, define the actual features (constants, types, functions...) you
113want to expose. If it's a constant, just add it to ``TYPES``::
114
115 static const int QM_TRANSMOGRIFICATION_ALIGNMENT_LEFT;
116 static const int QM_TRANSMOGRIFICATION_ALIGNMENT_RIGHT;
117
118If it's a struct, add it to ``TYPES`` as well. The following is an
119opaque struct::
120
121 typedef ... QM_TRANSMOGRIFICATION_CTX;
122
123... but you can also make some or all items in the struct accessible::
124
125 typedef struct {
126 /* Fundamental constant k for your particular universe */
127 BIGNUM *k;
128 ...;
129 } QM_TRANSMOGRIFICATION_CTX;
130
Paul Kehrer491fd7c2017-06-27 11:15:37 -1000131For functions just add the signature to ``FUNCTIONS``::
Laurens Van Houtvenefa5cfb2014-06-23 13:51:35 +0200132
133 int QM_transmogrify(QM_TRANSMOGRIFICATION_CTX *, int);
134
135Then, we define the ``CUSTOMIZATIONS`` entry. To do that, we have to
136come up with a C preprocessor expression that decides whether or not a
137feature exists in the library. For example::
138
139 #ifdef QM_transmogrify
140
141Then, we set the flag that signifies the feature exists::
142
143 static const long Cryptography_HAS_QUANTUM_TRANSMOGRIFICATION = 1;
144
145Otherwise, we set that flag to 0::
146
147 #else
148 static const long Cryptography_HAS_QUANTUM_TRANSMOGRIFICATION = 0;
149
Laurens Van Houtven316b4fd2014-06-23 16:33:24 +0200150Then, in that ``#else`` block, we define the names that aren't
151available as dummy values. For an integer constant, use 0::
Laurens Van Houtvenefa5cfb2014-06-23 13:51:35 +0200152
153 static const int QM_TRANSMOGRIFICATION_ALIGNMENT_LEFT = 0;
154 static const int QM_TRANSMOGRIFICATION_ALIGNMENT_RIGHT = 0;
155
156For a function, it's a bit trickier. You have to define a function
157pointer of the appropriate type to be NULL::
158
159 int (*QM_transmogrify)(QM_TRANSMOGRIFICATION_CTX *, int) = NULL;
160
Laurens Van Houtven1dc0b142014-06-23 13:55:21 +0200161(To do that, copy the signature, put a ``*`` in front of the function
Laurens Van Houtvena7b07582014-06-23 16:23:46 +0200162name and wrap it in parentheses, and then put ``= NULL`` at the end).
Laurens Van Houtvenefa5cfb2014-06-23 13:51:35 +0200163
164Note how types don't need to be conditionally defined, as long as all
Laurens Van Houtven4e74a7f2014-06-23 16:29:39 +0200165the necessarily type definitions are in place.
Laurens Van Houtvenefa5cfb2014-06-23 13:51:35 +0200166
167Finally, add an entry to ``CONDITIONAL_NAMES`` with all of the things
168you want to conditionally export::
169
Alex Gaynor601ed632017-07-08 20:35:02 -0400170 def cryptography_has_quantum_transmogrification():
171 return [
Laurens Van Houtvenefa5cfb2014-06-23 13:51:35 +0200172 "QM_TRANSMOGRIFICATION_ALIGNMENT_LEFT",
173 "QM_TRANSMOGRIFICATION_ALIGNMENT_RIGHT",
Alex Gaynor601ed632017-07-08 20:35:02 -0400174 "QM_transmogrify",
Laurens Van Houtvenefa5cfb2014-06-23 13:51:35 +0200175 ]
Alex Gaynor601ed632017-07-08 20:35:02 -0400176
177
178 CONDITIONAL_NAMES = {
179 ...
180 "Cryptography_HAS_QUANTUM_TRANSMOGRIFICATION": (
181 cryptography_has_quantum_transmogrification
182 ),
Laurens Van Houtvenefa5cfb2014-06-23 13:51:35 +0200183 }
184
Alex Gaynor601ed632017-07-08 20:35:02 -0400185
Laurens Van Houtvenefa5cfb2014-06-23 13:51:35 +0200186Caveats
187~~~~~~~
188
189Sometimes, a set of loosely related features are added in the same
190version, and it's impractical to create ``#ifdef`` statements for each
191one. In that case, it may make sense to either check for a particular
Alex Gaynor0e8cdf12016-12-13 21:05:35 -0500192version. For example, to check for OpenSSL 1.1.0 or newer::
Laurens Van Houtvenefa5cfb2014-06-23 13:51:35 +0200193
Alex Gaynor0e8cdf12016-12-13 21:05:35 -0500194 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
Laurens Van Houtvenefa5cfb2014-06-23 13:51:35 +0200195
Laurens Van Houtven260e8872014-06-23 16:22:32 +0200196Sometimes, the version of a library on a particular platform will have
Laurens Van Houtvenefa5cfb2014-06-23 13:51:35 +0200197features that you thought it wouldn't, based on its version.
198Occasionally, packagers appear to ship arbitrary VCS checkouts. As a
199result, sometimes you may have to add separate ``#ifdef`` statements
200for particular features. This kind of issue is typically only caught
201by running the tests on a wide variety of systems, which is the job of
202our continuous integration infrastructure.