66 Licensed under the AVSystem-5-clause License.
77 See the attached LICENSE file for details.
88
9- Bootstrapper (smart card bootstrap)
10- ===================================
9+ Bootstrapper and SIM bootstrap
10+ ==============================
1111
1212.. contents :: :local:
1313
@@ -28,11 +28,21 @@ between Smartcard and LwM2M Device Storage in `Appendix H
2828<http://www.openmobilealliance.org/release/LightweightM2M/V1_1_1-20190617-A/HTML-Version/OMA-TS-LightweightM2M_Core-V1_1_1-20190617-A.html#16-0-Appendix-H-Secure-channel-between-Smartcard-and-LwM2M-Device-Storage-for-secure-Bootstrap-Data-provisioning-Normative> `_
2929thereof.
3030
31- While communicating with the smart card is considered outside the scope for the
32- Anjay library, the "bootstrapper" feature, available commercially, implements a
33- parser for the file format described in `section G.3.4 of the Appendix G
34- <http://www.openmobilealliance.org/release/LightweightM2M/V1_1_1-20190617-A/HTML-Version/OMA-TS-LightweightM2M_Core-V1_1_1-20190617-A.html#15-3-4-0-G34-EF-LwM2M_Bootstrap> `_
35- mentioned above.
31+ The "bootstrapper" feature, available as a commercial extension to the Anjay
32+ library, includes two modules that aid in implementing this part of the
33+ specification:
34+
35+ * ``bootstrapper `` implements a parser for the file format described in
36+ `section G.3.4 of the Appendix G
37+ <http://www.openmobilealliance.org/release/LightweightM2M/V1_1_1-20190617-A/HTML-Version/OMA-TS-LightweightM2M_Core-V1_1_1-20190617-A.html#15-3-4-0-G34-EF-LwM2M_Bootstrap> `_
38+ mentioned above
39+ * ``sim_bootstrap `` implements the flow of `ISO/IEC 7816-4
40+ <https://www.iso.org/obp/ui/#iso:std:iso-iec:7816:-4:ed-4:v1:en> `_ commands
41+ necessary to retrieve the aforementioned file
42+
43+ With the above features in place, all that's left to implement is actual
44+ communication with the smart card, typically sending and receiving ``AT+CSIM ``
45+ commands to a cellular modem.
3646
3747Bootstrapping from smart card has a number of advantages, including:
3848
@@ -64,7 +74,42 @@ be used. The user will need to provide an implementation of ``avs_stream_t``
6474that allows the Anjay code to read the file contained on the smartcard. The
6575``avs_stream_simple_input_create() `` function from the `avs_stream_simple_io.h
6676<https://github.com/AVSystem/avs_commons/blob/master/include_public/avsystem/commons/avs_stream_simple_io.h> `_
67- header is likely to be the easiest way to provide such an implementation.
77+ header is likely to be the easiest way to provide such an implementation, aside
78+ from using the SIM bootstrap module described below.
79+
80+ Enabling and configuring the sim_bootstrap module
81+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
82+
83+ Similarly, to enable the sim_bootstrap module, you can enable the
84+ ``ANJAY_WITH_MODULE_SIM_BOOTSTRAP `` macro in the ``anjay_config.h `` file or, if
85+ using CMake, enable the corresponding ``WITH_MODULE_sim_bootstrap `` CMake
86+ option. This requires that the bootstrapper feature is also enabled.
87+
88+ By default, the module will access the PKCS#15 application directory file and
89+ search it for the EF(DODF-bootstrap) file in a way that is compliant with LwM2M
90+ TS Appendix G mentioned above.
91+
92+ However, you can override the OID of the file to look for, by defining the
93+ ``ANJAY_MODULE_SIM_BOOTSTRAP_DATA_OID_OVERRIDE_HEX `` macro in ``anjay_config.h ``
94+ or setting the corresponding ``MODULE_sim_bootstrap_DATA_OID_OVERRIDE_HEX ``
95+ CMake option. It shall be set to a string containing hexlified DER
96+ representation of the desired OID. The default, standards-compliant value is
97+ ``"672b0901" `` (which corresponds to OID 2.23.43.9.1), but you may need to
98+ change it to a different value, for example some cards are known to use a
99+ mistakenly encoded value of ``"0604672b0901" ``.
100+
101+ Alternatively, you might define the
102+ ``ANJAY_MODULE_SIM_BOOTSTRAP_HARDCODED_FILE_ID `` macro (or set the
103+ ``MODULE_sim_bootstrap_HARDCODED_FILE_ID `` CMake option) to bypass the directory
104+ search entirely and set a hardcoded file ID, e.g. ``0x6432 ``.
105+
106+ Once the module is enabled and configured, you can use the
107+ `anjay_sim_bootstrap_stream_create()
108+ <../api/sim__bootstrap_8h.html#a7cd497f30bfc7d36c6f0efb1db1d5a19> `_ function to
109+ create an input stream suitable for passing to ``anjay_bootstrapper() ``. In the
110+ simplest case, you can also use the `anjay_sim_bootstrap_perform()
111+ <../api/sim__bootstrap_8h.html#aa94114321f3af6532babde1efd9bdcec> `_ function
112+ that combines both calls and automatically closes the stream as well.
68113
69114Bootstrap information generator tool
70115^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -191,10 +236,11 @@ Example code
191236 commercial version of Anjay that includes the bootstrapper feature.
192237
193238The example is loosely based on the :doc: `../BasicClient/BC-MandatoryObjects `
194- tutorial. However, since the bootstrap information will be loaded from a file,
195- the ``setup_security_object() `` and ``setup_server_object() `` functions are no
196- longer necessary, and the calls to them can be replaced with direct calls to
197- `anjay_security_object_install()
239+ tutorial, and additionally borrows much of the modem communication code from
240+ :doc: `CF-NIDD `. Since the bootstrap information will be loaded from a smart
241+ card, the ``setup_security_object() `` and ``setup_server_object() `` functions
242+ are no longer necessary, and the calls to them can be replaced with direct calls
243+ to `anjay_security_object_install()
198244<../api/security_8h.html#a5fffaeedfc5c2933e58ac1446fd0401d> `_ and
199245`anjay_server_object_install()
200246<../api/server_8h.html#a36a369c0d7d1b2ad42c898ac47b75765> `_:
@@ -205,8 +251,7 @@ longer necessary, and the calls to them can be replaced with direct calls to
205251
206252 int main(int argc, char *argv[]) {
207253 if (argc != 3) {
208- avs_log(tutorial, ERROR, "usage: %s ENDPOINT_NAME BOOTSTRAP_INFO_FILE",
209- argv[0]);
254+ avs_log(tutorial, ERROR, "usage: %s ENDPOINT_NAME MODEM_PATH", argv[0]);
210255 return -1;
211256 }
212257
@@ -231,7 +276,7 @@ longer necessary, and the calls to them can be replaced with direct calls to
231276 }
232277
233278 if (!result) {
234- result = bootstrap_from_file (anjay, argv[2]);
279+ result = bootstrap_from_sim (anjay, argv[2]);
235280 }
236281
237282 if (!result) {
@@ -246,40 +291,140 @@ longer necessary, and the calls to them can be replaced with direct calls to
246291As you can see, the command line now expects a second argument with a name of
247292the file containing the bootstrap information.
248293
249- This file is loaded using the ``bootstrap_from_file () `` function, implemented as
294+ This file is loaded using the ``bootstrap_from_sim () `` function, implemented as
250295follows:
251296
252297.. highlight :: c
253298.. snippet-source :: examples/commercial-features/CF-SmartCardBootstrap/src/main.c
254299
255- static int bootstrap_from_file(anjay_t *anjay, const char *filename) {
256- avs_log(tutorial, INFO, "Attempting to bootstrap from file");
257-
258- avs_stream_t *file_stream =
259- avs_stream_file_create(filename, AVS_STREAM_FILE_READ);
260-
261- if (!file_stream) {
262- avs_log(tutorial, ERROR, "Could not open file");
300+ typedef struct {
301+ avs_buffer_t *buffer;
302+ } fifo_t;
303+
304+ // ...
305+
306+ typedef struct {
307+ fifo_t fifo;
308+ int pts_fd;
309+ } modem_ctx_t;
310+
311+ // ...
312+
313+ static int sim_perform_command(void *modem_ctx_,
314+ const void *cmd,
315+ size_t cmd_length,
316+ void *out_buf,
317+ size_t out_buf_size,
318+ size_t *out_response_size) {
319+ modem_ctx_t *modem_ctx = (modem_ctx_t *) modem_ctx_;
320+ char req_buf[REQ_BUF_SIZE];
321+ char resp_buf[RESP_BUF_SIZE] = "";
322+
323+ char *req_buf_ptr = req_buf;
324+ char *const req_buf_end = req_buf + sizeof(req_buf);
325+ int result = avs_simple_snprintf(req_buf_ptr,
326+ (size_t) (req_buf_end - req_buf_ptr),
327+ "AT+CSIM=%" PRIu32 ",\" ",
328+ (uint32_t) (2 * cmd_length));
329+ if (result < 0) {
330+ return result;
331+ }
332+ req_buf_ptr += result;
333+ if ((size_t) (req_buf_end - req_buf_ptr) < 2 * cmd_length) {
263334 return -1;
264335 }
265-
266- int result = 0;
267- if (avs_is_err(anjay_bootstrapper(anjay, file_stream))) {
268- avs_log(tutorial, ERROR, "Could not bootstrap from file");
269- result = -1;
336+ if ((result = avs_hexlify(req_buf_ptr, (size_t) (req_buf_end - req_buf_ptr),
337+ NULL, cmd, cmd_length))) {
338+ return result;
339+ }
340+ req_buf_ptr += 2 * cmd_length;
341+ if ((result = avs_simple_snprintf(
342+ req_buf_ptr, (size_t) (req_buf_end - req_buf_ptr), "\"\r\n "))
343+ < 0) {
344+ return result;
345+ }
346+ req_buf_ptr += result;
347+ ssize_t written =
348+ write(modem_ctx->pts_fd, req_buf, (size_t) (req_buf_ptr - req_buf));
349+ if (written != (ssize_t) (req_buf_ptr - req_buf)) {
350+ return -1;
270351 }
352+ avs_time_monotonic_t deadline = avs_time_monotonic_add(
353+ avs_time_monotonic_now(),
354+ avs_time_duration_from_scalar(5, AVS_TIME_S));
355+ bool csim_resp_received = false;
356+ bool ok_received = false;
357+ while (!ok_received) {
358+ if (modem_getline(modem_ctx, resp_buf, sizeof(resp_buf), deadline)) {
359+ return -1;
360+ }
361+ const char *resp_terminator = memchr(resp_buf, '\0 ', sizeof(resp_buf));
362+ if (!resp_terminator) {
363+ return -1;
364+ }
365+ if (memcmp(resp_buf, CSIM_RESP, strlen(CSIM_RESP)) == 0) {
366+ if (csim_resp_received) {
367+ return -1;
368+ }
369+ errno = 0;
370+ char *endptr = NULL;
371+ long long resp_reported_length =
372+ strtoll(resp_buf + strlen(CSIM_RESP), &endptr, 10);
373+ if (errno || !endptr || endptr[0] != ',' || endptr[1] != '"'
374+ || resp_reported_length < 0
375+ || endptr + resp_reported_length + 2 >= resp_terminator
376+ || endptr[resp_reported_length + 2] != '"'
377+ || avs_unhexlify(out_response_size, (uint8_t *) out_buf,
378+ out_buf_size, endptr + 2,
379+ (size_t) resp_reported_length)) {
380+ return -1;
381+ }
382+ csim_resp_received = true;
383+ } else if (strcmp(resp_buf, "OK") == 0) {
384+ ok_received = true;
385+ }
386+ }
387+ return csim_resp_received ? 0 : -1;
388+ }
389+
390+ static int bootstrap_from_sim(anjay_t *anjay, const char *modem_device) {
391+ modem_ctx_t modem_ctx = {
392+ .pts_fd = -1
393+ };
394+ int result = -1;
395+
396+ avs_log(tutorial, INFO, "Attempting to bootstrap from SIM card");
271397
272- avs_stream_cleanup(&file_stream);
398+ if (fifo_init(&modem_ctx.fifo)) {
399+ avs_log(tutorial, ERROR, "could not initialize FIFO");
400+ goto finish;
401+ }
402+ if ((modem_ctx.pts_fd = open(modem_device, O_RDWR)) < 0) {
403+ avs_log(tutorial, ERROR, "could not open modem device %s: %s",
404+ modem_device, strerror(errno));
405+ goto finish;
406+ }
407+ if (avs_is_err(anjay_sim_bootstrap_perform(anjay, sim_perform_command,
408+ &modem_ctx))) {
409+ avs_log(tutorial, ERROR, "Could not bootstrap from SIM card");
410+ goto finish;
411+ }
412+ result = 0;
413+ finish:
414+ if (modem_ctx.pts_fd >= 0) {
415+ close(modem_ctx.pts_fd);
416+ }
417+ fifo_destroy(&modem_ctx.fifo);
273418 return result;
274419 }
275420
276- This shares similarities with the ``restore_objects_if_possible() `` function
277- from the :doc: `../AdvancedTopics/AT-Persistence ` tutorial.
421+ The ``sim_perform_command() `` function is a callback that is passed to the
422+ ``sim_bootstrap `` module logic, and performs the ``AT+CSIM `` command over a
423+ serial port. The ``modem_getline() `` function it calls is almost identical to
424+ the one originally implemented for :doc: `CF-NIDD `.
278425
279- As mentioned in the :ref: `cf-smart-card-bootstrap-enabling ` section above, to
280- perform bootstrap using an actual smart card, the
281- ``avs_stream_simple_input_create() `` function from the `avs_stream_simple_io.h
282- <https://github.com/AVSystem/avs_commons/blob/master/include_public/avsystem/commons/avs_stream_simple_io.h> `_
283- header could be used instead of the ``avs_stream_file_create() `` call that is
284- used here to access regular file system.
426+ The ``bootstrap_from_sim() `` function itself is a wrapper over
427+ `anjay_sim_bootstrap_perform()
428+ <../api/sim__bootstrap_8h.html#aa94114321f3af6532babde1efd9bdcec> `_ that
429+ additionally initializes and closes the card communication channel.
285430
0 commit comments