Skip to content

Commit a92b146

Browse files
author
Mateusz Kwiatkowski
committed
Anjay 3.4.1
Features - (commercial feature only) New ``sim_bootstrap`` module that implements the logic necessary to extract the EF(DODF-bootstrap) file contents from a smart card Bugfixes - Fixed a potential crash in case of a specific out-of-memory condition in Advanced Firmware Update - Fixed `anjay_config_log.h` so that all non-binary configuration options are properly logged - Fixed a regression from 3.4.0 that prevented ``nsh_lwm2m.py`` from launching
1 parent de57869 commit a92b146

File tree

22 files changed

+915
-113
lines changed

22 files changed

+915
-113
lines changed

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
# Changelog
22

3+
## 3.4.1 (June 23rd, 2023)
4+
5+
### Features
6+
7+
- (commercial feature only) New ``sim_bootstrap`` module that implements the
8+
logic necessary to extract the EF(DODF-bootstrap) file contents from a smart
9+
card
10+
11+
### Bugfixes
12+
13+
- Fixed a potential crash in case of a specific out-of-memory condition in
14+
Advanced Firmware Update
15+
- Fixed `anjay_config_log.h` so that all non-binary configuration options are
16+
properly logged
17+
- Fixed a regression from 3.4.0 that prevented ``nsh_lwm2m.py`` from launching
18+
319
## 3.4.0 (June 14th, 2023)
420

521
### Features

CMakeLists.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
cmake_minimum_required(VERSION 3.6.0)
99

1010
project(anjay C)
11-
set(ANJAY_VERSION "3.4.0" CACHE STRING "Anjay library version")
11+
set(ANJAY_VERSION "3.4.1" CACHE STRING "Anjay library version")
1212
set(ANJAY_BINARY_VERSION 1.0.0)
1313

1414
set(ANJAY_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
@@ -495,7 +495,8 @@ add_library(anjay
495495
src/modules/server/anjay_server_transaction.c
496496
src/modules/server/anjay_server_transaction.h
497497
src/modules/server/anjay_server_utils.c
498-
src/modules/server/anjay_server_utils.h)
498+
src/modules/server/anjay_server_utils.h
499+
)
499500

500501
target_include_directories(anjay PUBLIC
501502
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include_public>

Doxyfile.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ EXTRACT_PRIVATE = NO
366366
# If the EXTRACT_STATIC tag is set to YES all static members of a file
367367
# will be included in the documentation.
368368

369-
EXTRACT_STATIC = NO
369+
EXTRACT_STATIC = YES
370370

371371
# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
372372
# defined locally in source files will be included in the documentation.

demo/advanced_firmware_update.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ handle_multipackage(FILE *f,
120120
return -1;
121121
}
122122
mm.package_len[i] = avs_convert_be32(mm.package_len[i]);
123-
if (mm.package_len == 0) {
123+
if (mm.package_len[i] == 0) {
124124
demo_log(
125125
ERROR,
126126
"Zero-length packages within multipackage not allowed");
@@ -133,7 +133,10 @@ handle_multipackage(FILE *f,
133133
} else {
134134
/* It is not multipackage, move stream to the beginning to easily handle
135135
* like standard package */
136-
fseek(f, 0L, SEEK_SET);
136+
if (fseek(f, 0L, SEEK_SET)) {
137+
demo_log(ERROR, "Could not seek in the multipackage file");
138+
return -1;
139+
}
137140
}
138141
return 0;
139142
}

doc/sphinx/snippet_sources.md5

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ ca82847266e70bd9728611eda7b1a19e deps/avs_commons/src/net/avs_net_impl.h
1717
4613be67a0c66ba130fe5601e75ef5f0 examples/commercial-features/CF-EST-PKCS11/src/main.c
1818
bcefabd7777e025ed9b0503b81365a59 examples/commercial-features/CF-EST/src/main.c
1919
0500194428e4129e06a8dd5fec2da08e examples/commercial-features/CF-NIDD/src/main.c
20-
4e6bd88507be2ec8dc27ca08cb319ac8 examples/commercial-features/CF-NIDD/src/nidd_demo_driver.c
20+
671d403eecc85d842c0faec745e8889c examples/commercial-features/CF-NIDD/src/nidd_demo_driver.c
2121
f7b0898ab6ee5fe363e45f010468c8bc examples/commercial-features/CF-OSCORE/src/main.c
2222
63981c17cad5eec1151b88e40655f780 examples/commercial-features/CF-PKCS11/src/main.c
2323
fabf0d50997dac05e8b3ddc63db092c9 examples/commercial-features/CF-PSA-bootstrap/src/main.c
2424
4202016537ea33e39cdf82cce280c3cd examples/commercial-features/CF-PSA-management/src/main.c
2525
1ae4a0f3eec2e64d3377c681a7ce51c9 examples/commercial-features/CF-PSA-PKI/src/main.c
2626
9e6292087532b87f0865b691a5f9a0db examples/commercial-features/CF-PSA-PSK/src/main.c
27-
25dc50d72d98e0faf95e193c3255cbb7 examples/commercial-features/CF-SmartCardBootstrap/src/main.c
27+
d95709d6755012199650d59a4250516c examples/commercial-features/CF-SmartCardBootstrap/src/main.c
2828
5ab384142e2cf91735e367fa4c8be9c3 examples/commercial-features/CF-SMS-PSK/src/main.c
2929
991f1cd2582963c8174bb4cd20e2bb29 examples/commercial-features/CF-SMS/src/main.c
3030
3e324c15570ac568c5efc079dd5139ba examples/commercial-features/CF-SMS-UDP/src/main.c

doc/sphinx/source/CommercialFeatures/CF-SmartCardBootstrap.rst

Lines changed: 183 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
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>`_
2929
thereof.
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

3747
Bootstrapping 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``
6474
that 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

69114
Bootstrap information generator tool
70115
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -191,10 +236,11 @@ Example code
191236
commercial version of Anjay that includes the bootstrapper feature.
192237

193238
The 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
246291
As you can see, the command line now expects a second argument with a name of
247292
the 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
250295
follows:
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

Comments
 (0)