Skip to content

Commit 050cf01

Browse files
committed
Merge branch 'master' into hardening
* master: Implement TR31_USE_SSCANF_DATETIME Update to latest libargp Fix various typos Fix ISO 8601 parsing using sscanf() when strptime() is unavailable Fix ASCII decimal to binary integer conversion Fix and improve parsing of IBM proprietary optional blocks Update Gentoo ebuild Update tr31-tool to use GPLv3 Update to latest libargp Improve readability of optional block PB export Fix ISO 8601 delimiter validation This merge is to confirm that the master branch resolves the remaining CodeQL items in the hardening branch.
2 parents ed6c5c6 + 73444ee commit 050cf01

File tree

11 files changed

+125
-60
lines changed

11 files changed

+125
-60
lines changed

.github/workflows/ubuntu-build.yaml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,34 @@ jobs:
4242
- name: Test
4343
run: ctest --test-dir build --output-on-failure -j 4
4444

45+
build-ubuntu-sscanf:
46+
name: Ubuntu (sscanf)
47+
runs-on: ubuntu-latest
48+
49+
steps:
50+
- name: Install dependencies
51+
run: |
52+
sudo apt-get update
53+
sudo apt-get install -y libmbedtls-dev
54+
55+
- name: Checkout
56+
uses: actions/checkout@v4
57+
with:
58+
submodules: recursive
59+
- run: git describe --always --dirty
60+
61+
- name: Configure CMake with sscanf() for date/time
62+
run: |
63+
cmake -B build \
64+
-DCMAKE_BUILD_TYPE="Debug" \
65+
-DTR31_USE_SSCANF_DATETIME=ON
66+
67+
- name: Build
68+
run: cmake --build build -j 4
69+
70+
- name: Test
71+
run: ctest --test-dir build --output-on-failure -j 4
72+
4573
build-ubuntu-sanitizers:
4674
strategy:
4775
fail-fast: false

README.md

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ This project began as an implementation of the ASC X9 TR-31 standard and has
1111
since grown to include the ANSI X9.143 standard which supersedes it, and the
1212
ISO 20038 standard which extends it. However, this project continues to refer
1313
to the library as TR-31 and prefixes the API, data types and command line tool
14-
with `tr31`, while mostly avoiding that naming when refering to key blocks and
14+
with `tr31`, while mostly avoiding that naming when referring to key blocks and
1515
data associated with key blocks. Given that most uses of these standards
1616
involve dedicated security hardware, this implementation is mostly for
1717
validation and debugging purposes.
@@ -55,9 +55,10 @@ Dependencies
5555
* TR-31 library requires [MbedTLS](https://github.com/Mbed-TLS/mbedtls)
5656
(preferred), or [OpenSSL](https://www.openssl.org/)
5757
* `tr31-tool` will be built by default and requires `argp` (either via Glibc, a
58-
system-provided standalone or a downloaded implementation; see
58+
system-provided standalone, or downloaded during the build from
59+
[libargp](https://github.com/leonlynch/libargp); see
5960
[MacOS / Windows](#macos--windows)). Use the `BUILD_TR31_TOOL` option to
60-
prevent TR-31 tool from being built and avoid the dependency on `argp`.
61+
prevent `tr31-tool` from being built and avoid the dependency on `argp`.
6162
* [Doxygen](https://github.com/doxygen/doxygen) can _optionally_ be used to
6263
generate API documentation if it is available; see
6364
[Documentation](#documentation)
@@ -216,5 +217,15 @@ License
216217

217218
Copyright 2020-2025 [Leon Lynch](https://github.com/leonlynch).
218219

219-
This project is licensed under the terms of the LGPL v2.1 license. See
220-
[LICENSE](https://github.com/openemv/tr31/blob/master/LICENSE) file.
220+
This project is licensed under the terms of the LGPL v2.1 license with the
221+
exception of `tr31-tool` which is licensed under the terms of the GPL v3
222+
license.
223+
See [LICENSE](https://github.com/openemv/tr31/blob/master/LICENSE) file.
224+
225+
This project includes [crypto](https://github.com/openemv/crypto) as a git
226+
submodule and it is licensed under the terms of the MIT license. See
227+
[LICENSE](https://github.com/openemv/crypto/blob/master/LICENSE) file.
228+
229+
This project may download [libargp](https://github.com/leonlynch/libargp)
230+
during the build and it is licensed under the terms of the LGPL v3 license. See
231+
[LICENSE](https://github.com/leonlynch/libargp/blob/master/LICENSE) file.

cmake/Modules/FetchLibArgp.cmake

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
##############################################################################
2-
# Copyright 2022-2023 Leon Lynch
2+
# Copyright 2022-2023, 2025 Leon Lynch
33
#
44
# This file is licensed under the terms of the LGPL v2.1 license.
55
# See LICENSE file.
@@ -24,7 +24,7 @@ message(CHECK_START "Downloading libargp...")
2424
FetchContent_Declare(
2525
libargp
2626
GIT_REPOSITORY https://github.com/leonlynch/libargp.git
27-
GIT_TAG 987d87b98e4cd03abb1107b77ef5d43ad0552e13
27+
GIT_TAG 0a0c4df3431d4f268076d5784080b5c5b6434b8f
2828
)
2929
FetchContent_MakeAvailable(libargp)
3030
message(CHECK_PASS "done")

gentoo/dev-libs/tr31/metadata.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@
1212
<use>
1313
<flag name="mbedtls">Use MbedTLS as the crypto library</flag>
1414
<flag name="openssl">Use OpenSSL as the crypto library</flag>
15+
<flag name="tools">Build command-line tool (tr31-tool)</flag>
1516
</use>
1617
</pkgmetadata>

gentoo/dev-libs/tr31/tr31-9999.ebuild

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 1999-2024 Gentoo Authors
1+
# Copyright 1999-2025 Gentoo Authors
22
# Distributed under the terms of the GNU General Public License v2
33

44
EAPI=8
@@ -15,10 +15,10 @@ else
1515
SRC_URI="https://github.com/openemv/tr31/releases/download/${PV}/${P}-src.tar.gz -> ${P}.tar.gz"
1616
fi
1717

18-
LICENSE="LGPL-2.1"
18+
LICENSE="LGPL-2.1+ tools? ( GPL-3+ )"
1919
SLOT="0"
2020
KEYWORDS="~amd64 ~x86"
21-
IUSE="+mbedtls openssl doc test"
21+
IUSE="+mbedtls openssl +tools doc test"
2222
REQUIRED_USE="|| ( mbedtls openssl )"
2323
RESTRICT="!test? ( test )"
2424

@@ -45,6 +45,7 @@ src_configure() {
4545
local mycmakeargs=(
4646
$(cmake_use_find_package mbedtls MbedTLS)
4747
$(cmake_use_find_package openssl OpenSSL)
48+
-DBUILD_TR31_TOOL=$(usex tools)
4849
-DBUILD_DOCS=$(usex doc)
4950
-DBUILD_TESTING=$(usex test)
5051
)

src/CMakeLists.txt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,14 @@ else()
116116
# set as cache entry to persist across multiple builds
117117
set(TR31_ENABLE_DATETIME_CONVERSION OFF CACHE INTERNAL "Date/time conversion availability")
118118
endif()
119+
# option to use sscanf() for date/time parsing even when strptime() is
120+
# available
121+
option(TR31_USE_SSCANF_DATETIME "Use sscanf() for date/time parsing")
122+
if(HAVE_STRPTIME AND NOT TR31_USE_SSCANF_DATETIME)
123+
message(STATUS "Using strptime() for date/time parsing")
124+
else()
125+
message(STATUS "Using sscanf() for date/time parsing")
126+
endif()
119127

120128
include(GNUInstallDirs) # provides CMAKE_INSTALL_* variables and good defaults for install()
121129

@@ -905,7 +913,7 @@ if(TARGET tr31-tool AND BUILD_TESTING)
905913
)
906914

907915
# test IBM proprietary key block
908-
# NOTE: the input was hand crafted based on https://www.ibm.com/docs/en/zos/3.1.0?topic=ktf-x9143-tr-31-key-block-header-optional-block-data and https://www.ibm.com/docs/en/linux-on-systems?topic=data-tr-31-optional-block
916+
# NOTE: the input was hand crafted based on https://www.ibm.com/docs/en/zos/3.2.0?topic=ktf-x9143-tr-31-key-block-header-optional-block-data and https://www.ibm.com/docs/en/linux-on-systems?topic=data-tr-31-optional-block
909917
add_test(NAME tr31_tool_test53
910918
COMMAND tr31-tool --import D014410A100N0200101CIBMC01140123456789ABCDEFPB04000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 --import-no-strict-validation
911919
)

src/tr31-tool.c

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,18 @@
33
*
44
* Copyright 2020-2025 Leon Lynch
55
*
6-
* This program is free software; you can redistribute it and/or
7-
* modify it under the terms of the GNU Lesser General Public
8-
* License as published by the Free Software Foundation; either
9-
* version 2.1 of the License, or (at your option) any later version.
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
1010
*
1111
* This program is distributed in the hope that it will be useful,
1212
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14-
* Lesser General Public License for more details.
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
1515
*
16-
* You should have received a copy of the GNU Lesser General Public
17-
* License along with this program. If not, see
18-
* <https://www.gnu.org/licenses/>.
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
1918
*/
2019

2120
#include "tr31.h"
@@ -162,7 +161,7 @@ static struct argp_option argp_options[] = {
162161
{ "export-opt-block-KC", TR31_TOOL_OPTION_EXPORT_OPT_BLOCK_KC, NULL, 0, "Add optional block KC (KCV of wrapped key) during key block export. May be used with either --export-template or --export-header." },
163162
{ "export-opt-block-KP", TR31_TOOL_OPTION_EXPORT_OPT_BLOCK_KP, NULL, 0, "Add optional block KP (KCV of KBPK) during key block export. May be used with either --export-template or --export-header." },
164163
{ "export-opt-block-KS", TR31_TOOL_OPTION_EXPORT_OPT_BLOCK_KS, "IKSN", 0, "Add optional block KS (Initial Key Serial Number) during key block export. May be used with either --export-template or --export-header." },
165-
{ "export-opt-block-LB", TR31_TOOL_OPTION_EXPORT_OPT_BLOCK_LB, "ASCII", 0, "Add optinal block LB (Label) during key block export. May be used with either --export-template or --export-header." },
164+
{ "export-opt-block-LB", TR31_TOOL_OPTION_EXPORT_OPT_BLOCK_LB, "ASCII", 0, "Add optional block LB (Label) during key block export. May be used with either --export-template or --export-header." },
166165
{ "export-opt-block-PK", TR31_TOOL_OPTION_EXPORT_OPT_BLOCK_PK, "KCV", 0, "Add optional block PK (Protection Key Check Value) during key block export. May be used with either --export-template or --export-header." },
167166
{ "export-opt-block-TC", TR31_TOOL_OPTION_EXPORT_OPT_BLOCK_TC, "ISO8601", 0, "Add optional block TC (Time of Creation in ISO 8601 UTC format) during key block export. May be used with either --export-template or --export-header. Specify \"now\" for current date/time." },
168167
{ "export-opt-block-TS", TR31_TOOL_OPTION_EXPORT_OPT_BLOCK_TS, "ISO8601", 0, "Add optional block TS (Time Stamp in ISO 8601 UTC format) during key block export. May be used with either --export-template or --export-header. Specify \"now\" for current date/time." },
@@ -454,7 +453,7 @@ static error_t argp_parser_helper(int key, char* arg, struct argp_state* state)
454453
}
455454
for (size_t i = 0; i < strlen(arg); ++i) {
456455
if (!isalnum(arg[i])) {
457-
argp_error(state, "Export optional block DA consist of alphanumeric characters (invalid character '%c' is not allowed)", arg[i]);
456+
argp_error(state, "Export optional block DA must consist of alphanumeric characters (invalid character '%c' is not allowed)", arg[i]);
458457
}
459458
}
460459
options->export_opt_block_DA = arg;

src/tr31.c

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ static int dec_to_int(const char* str, size_t str_len)
153153

154154
value = 0;
155155
for (size_t i = 0; i < str_len; ++i) {
156-
if (str[i] < '0' && str[i] > '9') {
156+
if (str[i] < '0' || str[i] > '9') {
157157
return -1;
158158
}
159159

@@ -855,7 +855,7 @@ int tr31_init_from_header(
855855
ptr += opt_blk_len;
856856
}
857857

858-
// NOTE: the total optional block length is intentially ignored and not
858+
// NOTE: the total optional block length is intentionally ignored and not
859859
// validated against the encryption block length
860860

861861
// success
@@ -2559,11 +2559,13 @@ int tr31_export(
25592559
// So we'll use the encryption block size which is determined by the key
25602560
// block format version.
25612561
if (opt_blk_len_total & (state.enc_block_size-1)) {
2562-
unsigned int pb_len = 4; // Minimum length of optional block PB
2562+
// minimum length of optional block PB
2563+
const unsigned int pb_min_len = sizeof(struct tr31_opt_blk_hdr_t);
2564+
unsigned int pb_len = pb_min_len;
25632565

25642566
// compute required padding length
2565-
if ((opt_blk_len_total + pb_len) & (state.enc_block_size-1)) { // if further padding is required
2566-
pb_len = ((opt_blk_len_total + 4 + state.enc_block_size) & ~(state.enc_block_size-1)) - opt_blk_len_total;
2567+
if ((opt_blk_len_total + pb_min_len) & (state.enc_block_size-1)) { // if further padding is required
2568+
pb_len = ((opt_blk_len_total + pb_min_len + state.enc_block_size) & ~(state.enc_block_size-1)) - opt_blk_len_total;
25672569
}
25682570

25692571
if (ptr + pb_len - (void*)header > key_block_buf_len) {
@@ -2890,6 +2892,9 @@ static int tr31_opt_block_parse(
28902892

28912893
static int tr31_opt_block_validate_iso8601(const char* str, size_t str_len)
28922894
{
2895+
// length of optional block header used for ISO 8601 values
2896+
const unsigned int opt_blk_hdr_len = sizeof(struct tr31_opt_blk_hdr_t);
2897+
28932898
if (!str) {
28942899
return -1;
28952900
}
@@ -2901,10 +2906,10 @@ static int tr31_opt_block_validate_iso8601(const char* str, size_t str_len)
29012906
// validate ISO 8601 string length
29022907
// see ANSI X9.143:2021, 6.3.6.13, table 21
29032908
// see ANSI X9.143:2021, 6.3.6.14, table 22
2904-
if (str_len != 0x13 - 4 && // no delimiters, ss precision
2905-
str_len != 0x15 - 4 && // no delimiters, ssss precision
2906-
str_len != 0x18 - 4 && // delimiters, ss precision
2907-
str_len != 0x1B - 4 // delimiters, ss.ss precision
2909+
if (str_len != 0x13 - opt_blk_hdr_len && // no delimiters, ss precision
2910+
str_len != 0x15 - opt_blk_hdr_len && // no delimiters, ssss precision
2911+
str_len != 0x18 - opt_blk_hdr_len && // delimiters, ss precision
2912+
str_len != 0x1B - opt_blk_hdr_len // delimiters, ss.ss precision
29082913
) {
29092914
return TR31_ERROR_INVALID_OPTIONAL_BLOCK_DATA;
29102915
}
@@ -2917,7 +2922,8 @@ static int tr31_opt_block_validate_iso8601(const char* str, size_t str_len)
29172922
}
29182923

29192924
// validate ISO 8601 delimiters (YYYY-MM-DDThh:mm:ss[.ss])
2920-
if (str_len == 0x18 || str_len == 0x1B) {
2925+
if (str_len == 0x18 - opt_blk_hdr_len ||
2926+
str_len == 0x1B - opt_blk_hdr_len) {
29212927
if (str[4] != '-' ||
29222928
str[7] != '-' ||
29232929
str[10] != 'T' ||
@@ -2927,7 +2933,7 @@ static int tr31_opt_block_validate_iso8601(const char* str, size_t str_len)
29272933
return TR31_ERROR_INVALID_OPTIONAL_BLOCK_DATA;
29282934
}
29292935
}
2930-
if (str_len == 0x1B) {
2936+
if (str_len == 0x1B - opt_blk_hdr_len) {
29312937
if (str[19] != '.') {
29322938
return TR31_ERROR_INVALID_OPTIONAL_BLOCK_DATA;
29332939
}
@@ -3015,18 +3021,24 @@ static int tr31_opt_block_export_PB(
30153021
struct tr31_opt_blk_t* opt_blk
30163022
)
30173023
{
3024+
if (pb_len < sizeof(*opt_blk)) {
3025+
// this should never happen
3026+
return -1;
3027+
}
3028+
const size_t pb_data_len = pb_len - sizeof(*opt_blk);
3029+
30183030
opt_blk->id = htons(TR31_OPT_BLOCK_PB);
30193031
int_to_hex(pb_len, opt_blk->length, sizeof(opt_blk->length));
30203032

30213033
if ((state->flags & TR31_EXPORT_ZERO_OPT_BLOCK_PB) == 0) {
30223034
// populate with random data and then transpose to the required range
3023-
crypto_rand(opt_blk->data, pb_len - 4);
3035+
crypto_rand(opt_blk->data, pb_data_len);
30243036
} else {
30253037
// populate with zeros instead of random data
3026-
memset(opt_blk->data, 0, pb_len - 4);
3038+
memset(opt_blk->data, 0, pb_data_len);
30273039
}
30283040

3029-
for (size_t i = 0; i < pb_len - 4; ++i) {
3041+
for (size_t i = 0; i < pb_data_len; ++i) {
30303042
// although optional block PB may contain printable ASCII characters in
30313043
// the range 0x20 to 0x7E, characters outside the ranges of '0'-'9',
30323044
// 'A'-'Z' and 'a'-'z' are problematic when using HSM protocols that
@@ -3047,8 +3059,8 @@ static int tr31_opt_block_export_PB(
30473059
} else if (tmp < 62) {
30483060
opt_blk->data[i] = tmp - 36 + 'a'; // 'a'-'z'
30493061
} else {
3050-
// This should never happen
3051-
return -1;
3062+
// this should never happen
3063+
return -2;
30523064
}
30533065
}
30543066

src/tr31.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* @file tr31.h
33
* @brief High level TR-31 library interface
44
*
5-
* Copyright 2020-2023 Leon Lynch
5+
* Copyright 2020-2023, 2025 Leon Lynch
66
*
77
* This library is free software; you can redistribute it and/or
88
* modify it under the terms of the GNU Lesser General Public
@@ -378,7 +378,7 @@ struct tr31_opt_blk_kcv_data_t {
378378
* @see @ref optional-block-wp-values "Wrapping Pedigree (WP) optional block values"
379379
*/
380380
struct tr31_opt_blk_wp_data_t {
381-
uint8_t version; ///<Wrapping Pedigree (WP) format version
381+
uint8_t version; ///< Wrapping Pedigree (WP) format version
382382
/// Wrapping Pedigree (WP) version 0
383383
struct v0_t {
384384
uint8_t wrapping_pedigree; ///< Wrapping Pedigree value
@@ -778,7 +778,7 @@ int tr31_opt_block_decode_HM(
778778
*
779779
* @param ctx Key block context object
780780
* @param ikid Initial Key Identifier (IKID) for Initial AES DUKPT Key
781-
* @param ikid_len of @p ikid in bytes. Must be 8 bytes.
781+
* @param ikid_len Length of @p ikid in bytes. Must be 8 bytes.
782782
* @return Zero for success. Less than zero for internal error. Greater than zero for data error. See @ref tr31_error_t
783783
*/
784784
int tr31_opt_block_add_IK(
@@ -796,7 +796,7 @@ int tr31_opt_block_add_IK(
796796
*
797797
* @param opt_ctx Optional block context object
798798
* @param ikid Initial Key Identifier (IKID) output
799-
* @param ikid_len of @p ikid in bytes. Must be 8 bytes.
799+
* @param ikid_len Length of @p ikid in bytes. Must be 8 bytes.
800800
* @return Zero for success. Less than zero for internal error. Greater than zero for data error. See @ref tr31_error_t
801801
*/
802802
int tr31_opt_block_decode_IK(
@@ -935,7 +935,7 @@ int tr31_opt_block_add_LB(
935935
*
936936
* @param ctx Key block context object
937937
* @param kcv_algorithm KCV algorithm. Either @ref TR31_OPT_BLOCK_KCV_LEGACY or @ref TR31_OPT_BLOCK_KCV_CMAC.
938-
* @param kcv Key Check Value (KCV) according to algoritm specified by @p kcv_algorithm
938+
* @param kcv Key Check Value (KCV) according to algorithm specified by @p kcv_algorithm
939939
* @param kcv_len Length of @p kcv in bytes. Must comply with ANSI X9.24-1 Annex A.
940940
* @return Zero for success. Less than zero for internal error. Greater than zero for data error. See @ref tr31_error_t
941941
*/

src/tr31_config.h.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,6 @@
3535
#cmakedefine HAVE_LOCALTIME_R
3636
#cmakedefine HAVE_GMTIME_R
3737
#cmakedefine TR31_ENABLE_DATETIME_CONVERSION
38+
#cmakedefine TR31_USE_SSCANF_DATETIME
3839

3940
#endif

0 commit comments

Comments
 (0)