pax_global_header00006660000000000000000000000064150476566540014534gustar00rootroot0000000000000052 comment=c2fcf5e4b21c712d54e35a11da2ad9ad134fb821 estkme-group-lpac-c2fcf5e/000077500000000000000000000000001504765665400156525ustar00rootroot00000000000000estkme-group-lpac-c2fcf5e/.clang-format000066400000000000000000000003111504765665400202200ustar00rootroot00000000000000Language: Cpp BasedOnStyle: LLVM IndentWidth: 4 ColumnLimit: 120 AlignEscapedNewlines: Left BreakBeforeBinaryOperators: NonAssignment IndentPPDirectives: AfterHash # vim: ft=yaml # kate: syntax yaml; estkme-group-lpac-c2fcf5e/.editorconfig000066400000000000000000000003631504765665400203310ustar00rootroot00000000000000root = true [*] charset = utf-8 indent_size = 4 indent_style = space end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true [*.md] indent_size = 2 trim_trailing_whitespace = false [.github/workflows/*] indent_size = 2 estkme-group-lpac-c2fcf5e/.git-blame-ignore-revs000066400000000000000000000001461504765665400217530ustar00rootroot00000000000000# chore(format): reformat all code 2025-08-15 23:11:39 +0800 62f54d429a028c69361af80151fd46782236a1c3 estkme-group-lpac-c2fcf5e/.github/000077500000000000000000000000001504765665400172125ustar00rootroot00000000000000estkme-group-lpac-c2fcf5e/.github/scripts/000077500000000000000000000000001504765665400207015ustar00rootroot00000000000000estkme-group-lpac-c2fcf5e/.github/scripts/build-ci.sh000077500000000000000000000043551504765665400227370ustar00rootroot00000000000000#!/bin/bash # This script is only for GitHub Actions use set -euo pipefail SCRIPT_DIR="$(dirname -- "${BASH_SOURCE[0]}")" source "$SCRIPT_DIR/functions.sh" BUILD="$(mktemp -d)" ARTIFACT="$WORKSPACE/build" mkdir -p "$BUILD/output" mkdir -p "$ARTIFACT" trap 'rm -rf '"$BUILD" EXIT cd "$BUILD" case "${1:-}" in make) cmake "$WORKSPACE" make -j copy-license "$BUILD/output" copy-usage "$BUILD/output" create-bundle "$ARTIFACT/lpac-$KERNEL-$MACHINE.zip" "$BUILD/output" ;; make-qmi) cmake "$WORKSPACE" -DLPAC_WITH_APDU_QMI=ON -DLPAC_WITH_APDU_QMI_QRTR=ON -DLPAC_WITH_APDU_MBIM=ON make -j copy-license "$BUILD/output" copy-usage "$BUILD/output" create-bundle "$ARTIFACT/lpac-$KERNEL-$MACHINE-with-qmi.zip" "$BUILD/output" ;; make-gbinder) cmake "$WORKSPACE" -DLPAC_WITH_APDU_GBINDER=ON make -j copy-license "$BUILD/output" copy-usage "$BUILD/output" create-bundle "$ARTIFACT/lpac-$KERNEL-$MACHINE-with-gbinder.zip" "$BUILD/output" ;; make-without-lto) cmake -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=OFF "$WORKSPACE" make -j copy-license "$BUILD/output" copy-usage "$BUILD/output" create-bundle "$ARTIFACT/lpac-$KERNEL-$MACHINE-without-lto.zip" "$BUILD/output" ;; debian) cmake "$WORKSPACE" -DCPACK_GENERATOR=DEB make -j package cp lpac_*.deb "$ARTIFACT" ;; mingw) cmake "$WORKSPACE" -DCMAKE_TOOLCHAIN_FILE=./cmake/linux-mingw64.cmake make -j copy-license "$BUILD/output" copy-curl-win "$BUILD/output" copy-usage "$BUILD/output" create-bundle "$ARTIFACT/lpac-windows-x86_64-mingw.zip" "$BUILD/output" ;; woa-mingw) cmake "$WORKSPACE" -DCMAKE_TOOLCHAIN_FILE=./cmake/linux-mingw64-woa.cmake make -j copy-license "$BUILD/output" copy-curl-woa "$BUILD/output" copy-usage "$BUILD/output" create-bundle "$ARTIFACT/lpac-windows-arm64-mingw.zip" "$BUILD/output" ;; woa-zig) cmake "$WORKSPACE" -DCMAKE_TOOLCHAIN_FILE=./cmake/aarch64-windows-zig.cmake make -j copy-license "$BUILD/output" copy-curl-woa "$BUILD/output" copy-usage "$BUILD/output" create-bundle "$ARTIFACT/lpac-windows-arm64-zig.zip" "$BUILD/output" ;; *) echo "Usage: $0 {make,debian,mingw,woa-mingw,woa-zig}" exit 1 ;; esac estkme-group-lpac-c2fcf5e/.github/scripts/functions.sh000066400000000000000000000036601504765665400232520ustar00rootroot00000000000000#!/bin/bash # This script is only for GitHub Actions use set -euo pipefail KERNEL="$(uname -s)" MACHINE="$(uname -m)" export KERNEL MACHINE export WORKSPACE="${GITHUB_WORKSPACE:-$(pwd)}" export CURL_VERSION="8.6.0_1" export MINGW_CURL_WIN64_BLOB="https://curl.se/windows/dl-$CURL_VERSION/curl-$CURL_VERSION-win64-mingw.zip" export MINGW_CURL_WIN64A_BLOB="https://curl.se/windows/dl-$CURL_VERSION/curl-$CURL_VERSION-win64a-mingw.zip" case "$KERNEL" in Linux) KERNEL="linux" ;; Darwin) KERNEL="darwin" MACHINE="universal" ;; esac function download { local URL SAVED_PATH SAVED_DIR URL="$1" SAVED_PATH="$(mktemp)" SAVED_DIR="$(mktemp -d)" wget --no-verbose "$URL" -O "$SAVED_PATH" case "$URL" in *.zip) unzip -q -d "$SAVED_DIR" "$SAVED_PATH" rm "$SAVED_PATH" echo "$SAVED_DIR" ;; *) echo "$SAVED_PATH" ;; esac } function copy-license { local OUTPUT="$1" cp "$WORKSPACE/src/LICENSE" "$OUTPUT/LICENSE-lpac" cp "$WORKSPACE/euicc/LICENSE" "$OUTPUT/LICENSE-libeuicc" cp "$WORKSPACE/cjson/LICENSE" "$OUTPUT/LICENSE-cjson" cp "$WORKSPACE/dlfcn-win32/LICENSE" "$OUTPUT/LICENSE-dlfcn-win32" } function copy-curl-woa { local OUTPUT="$1" CURL="$(download "$MINGW_CURL_WIN64A_BLOB")" cp "$CURL"/curl-*-mingw/bin/libcurl-arm64.dll "$OUTPUT/libcurl.dll" cp "$CURL"/curl-*-mingw/COPYING.txt "$OUTPUT/LICENSE-libcurl" rm -rf "$CURL" } function copy-curl-win { local OUTPUT="$1" CURL="$(download "$MINGW_CURL_WIN64_BLOB")" cp "$CURL"/curl-*-mingw/bin/libcurl-x64.dll "$OUTPUT/libcurl.dll" cp "$CURL"/curl-*-mingw/COPYING.txt "$OUTPUT/LICENSE-libcurl" rm -rf "$CURL" } function copy-usage { local OUTPUT="$1" cp "$WORKSPACE/docs/USAGE.md" "$OUTPUT/README.md" } function create-bundle { local BUNDLE_FILE="$1" local INPUT_DIR="$2" pushd "$INPUT_DIR" zip -r "$BUNDLE_FILE" ./* popd } estkme-group-lpac-c2fcf5e/.github/scripts/setup-debian.sh000077500000000000000000000016751504765665400236310ustar00rootroot00000000000000#!/bin/bash # This script is only for GitHub Actions use set -euo pipefail SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")" function apt() { sudo DEBIAN_PRIORITY=critical DEBIAN_FRONTEND=noninteractive \ apt-get -qq -o=Dpkg::Use-Pty=0 "$@" } apt update apt install -y build-essential libpcsclite-dev libcurl4-openssl-dev zip function setup-mingw-woarm64() { BASE_URL="https://github.com/Windows-on-ARM-Experiments/mingw-woarm64-build" FILENAME="aarch64-w64-mingw32-msvcrt-toolchain.tar.gz" VERSION="2024-02-08" SAVED_PATH="$(mktemp --suffix .tar.gz)" SAVED_DIR="$(mktemp -d)" wget -nv "$BASE_URL/releases/download/$VERSION/$FILENAME" -O "$SAVED_PATH" tar -C "$SAVED_DIR" -xaf "$SAVED_PATH" echo "$SAVED_DIR/bin" >> "$GITHUB_PATH" } case "${1:-}" in woa-mingw) setup-mingw-woarm64 ;; make-qmi) exec "$SCRIPT_DIR/setup-qmi.sh" ;; mingw) apt install -y gcc-mingw-w64 g++-mingw-w64 ;; esac estkme-group-lpac-c2fcf5e/.github/scripts/setup-qmi.sh000077500000000000000000000015561504765665400231730ustar00rootroot00000000000000#!/bin/bash # This script is only for GitHub Actions use set -euo pipefail function apt() { sudo DEBIAN_PRIORITY=critical DEBIAN_FRONTEND=noninteractive \ apt-get -qq -o=Dpkg::Use-Pty=0 "$@" } apt install -y libqrtr-glib-dev libmbim-glib-dev TMPDIR="$(mktemp -d)" trap 'rm -vrf '"$TMPDIR" EXIT # https://launchpad.net/libqmi QMI_VERSION="1.36.0-1_$(dpkg --print-architecture)" wget -nv -P "$TMPDIR" -i - <> $GITHUB_ENV - name: Upload ${{ matrix.build.name }} to Artifact uses: actions/upload-artifact@v4 with: name: lpac-${{ env.SHA7 }}-${{ matrix.build.artifact }} path: ${{ github.workspace }}/build/*.* release: name: Release runs-on: ubuntu-24.04 if: startsWith(github.ref, 'refs/tags/v') needs: build permissions: contents: write steps: - name: Download Artifact uses: actions/download-artifact@v5 with: merge-multiple: true pattern: "*" - name: Run SHA1SUM id: checksum run: | echo 'sha1sum<> $GITHUB_OUTPUT sha1sum * >> $GITHUB_OUTPUT echo 'EOF' >> $GITHUB_OUTPUT - name: Release uses: softprops/action-gh-release@v2 with: body: | ```plain ${{ steps.checksum.outputs.sha1sum }} ``` append_body: true files: "*" estkme-group-lpac-c2fcf5e/.github/workflows/clang-format-check.yaml000066400000000000000000000005701504765665400255620ustar00rootroot00000000000000name: clang-format Check on: [push, pull_request] jobs: formatting-check: name: Formatting Check runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - name: Run clang-format style check. uses: jidicula/clang-format-action@v4.15.0 with: clang-format-version: "20" exclude-regex: "(cjson|dlfcn-win32)/.*" estkme-group-lpac-c2fcf5e/.github/workflows/reuse-lint.yaml000066400000000000000000000006371504765665400242300ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2022 Free Software Foundation Europe e.V. # # SPDX-License-Identifier: CC0-1.0 --- name: REUSE Compliance Check on: [push, pull_request] permissions: contents: read jobs: reuse-compliance-check: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v5 - name: REUSE Compliance Check uses: fsfe/reuse-action@v5 estkme-group-lpac-c2fcf5e/.gitignore000066400000000000000000000005201504765665400176370ustar00rootroot00000000000000CMakeLists.txt.user CMakeCache.txt CMakeFiles CMakeScripts Testing Makefile cmake_install.cmake install_manifest.txt compile_commands.json CTestTestfile.cmake _deps .vscode* .DS_Store # clangd per-project index .cache/clangd # version /src/version.h # for package files lpac_*.deb lpac-*.zip # for clion ignores .idea cmake-build-* estkme-group-lpac-c2fcf5e/CMakeLists.txt000066400000000000000000000035141504765665400204150ustar00rootroot00000000000000cmake_minimum_required (VERSION 3.15) project (lpac VERSION 2.3.0 HOMEPAGE_URL "https://github.com/estkme-group/lpac" DESCRIPTION "C-based eUICC LPA." LANGUAGES C) set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD_REQUIRED ON) set(LPAC_CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") list(APPEND CMAKE_MODULE_PATH ${LPAC_CMAKE_MODULE_PATH}) if(NOT PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR) # Git auto-ignore out-of-source build directory file(GENERATE OUTPUT .gitignore CONTENT "*") endif() set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # add_compile_options(-Wall -Wextra -Wpedantic) # Enable LTO when possible. include(CheckIPOSupported) check_ipo_supported(RESULT result OUTPUT output) if(result) if(NOT DEFINED CMAKE_INTERPROCEDURAL_OPTIMIZATION) set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) endif() else() message(INFO "IPO is not supported: ${output}") endif() if (APPLE) set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64") endif() if(UNIX) include(GNUInstallDirs) if(NOT CMAKE_INSTALL_RPATH) set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_FULL_LIBDIR}/lpac") endif() endif() if(WIN32) add_subdirectory(dlfcn-win32) set(DL_LIBRARY dlfcn-win32) else() set(DL_LIBRARY dl) endif() if(CPACK_GENERATOR) set(CPACK_PACKAGE_VENDOR "eSTK.me Group") set(CPACK_DEBIAN_PACKAGE_MAINTAINER "eSTK.me Group") set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6") set(CPACK_DEBIAN_PACKAGE_RECOMMENDS "libcurl, libpcsclite, pcscd") set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT) set(CPACK_RPM_PACKAGE_LICENSE "AGPL-3.0-only AND LGPL-2.0-only") set(CPACK_RPM_PACKAGE_AUTOREQ "yes") set(CPACK_RPM_PACKAGE_REQUIRES "libcurl, libpcsclite, pcscd") include(CPack) endif() add_subdirectory(cjson) add_subdirectory(euicc) add_subdirectory(utils) add_subdirectory(driver) add_subdirectory(src) estkme-group-lpac-c2fcf5e/LICENSES/000077500000000000000000000000001504765665400170575ustar00rootroot00000000000000estkme-group-lpac-c2fcf5e/LICENSES/AGPL-3.0-only.txt000066400000000000000000001023441504765665400216240ustar00rootroot00000000000000GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software. A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public. The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version. An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU Affero General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Remote Network Interaction; Use with the GNU General Public License. Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU Affero General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements. You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see . estkme-group-lpac-c2fcf5e/LICENSES/CC0-1.0.txt000066400000000000000000000156101504765665400204640ustar00rootroot00000000000000Creative Commons Legal Code CC0 1.0 Universal CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; ii. moral rights retained by the original author(s) and/or performer(s); iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; v. rights protecting the extraction, dissemination, use and reuse of data in a Work; vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. estkme-group-lpac-c2fcf5e/LICENSES/LGPL-2.1-only.txt000066400000000000000000000626211504765665400216420ustar00rootroot00000000000000GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. one line to give the library's name and an idea of what it does. Copyright (C) year name of author This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. signature of Ty Coon, 1 April 1990 Ty Coon, President of Vice That's all there is to it! estkme-group-lpac-c2fcf5e/LICENSES/LicenseRef-ESTKME-Commercial.txt000066400000000000000000000001641504765665400246770ustar00rootroot00000000000000Non-public commercial license, please contact ESTKME TECHNOLOGY LIMITED, Hong Kong for details via inquiry@estk.me. estkme-group-lpac-c2fcf5e/LICENSES/MIT.txt000066400000000000000000000021121504765665400202450ustar00rootroot00000000000000MIT License Copyright (c) 2023-2025 ESTKME TECHNOLOGY LIMITED, Hong Kong Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. estkme-group-lpac-c2fcf5e/README.md000066400000000000000000000033401504765665400171310ustar00rootroot00000000000000# lpac lpac is a cross-platform local profile agent program, compatible with [SGP.22 version 2.2.2](https://www.gsma.com/solutions-and-impact/technologies/esim/wp-content/uploads/2020/06/SGP.22-v2.2.2.pdf). Features: - Support Activation Code and Confirmation Code - Support Custom IMEI sent to server - Support Profile Discovery (SM-DS) - Profile management: list, enable, disable, delete and nickname - Notification management: list, send and delete - Lookup eUICC chip info - etc ## Usage You can download lpac from [GitHub Release][latest], and read [USAGE](docs/USAGE.md) to use it. If you can't run it you need to compile by yourself, see also [DEVELOPERS](docs/DEVELOPERS.md). If you want to known which Linux distributions include lpac, see also [LINUX-DIST](docs/LINUX-DIST.md). If you have any issue, please read [FAQ](docs/FAQ.md) first. [latest]: https://github.com/estkme-group/lpac/releases/latest ## Software Ecosystem - [EasyLPAC] (Windows, Linux and macOS) - [{Open,Easy}EUICC][openeuicc] ([Mirror][openeuicc-mirror], Android) - [eSIM Manager (lpa-gtk)](https://codeberg.org/lucaweiss/lpa-gtk) (Linux Mobile) - [rlpa-server](https://github.com/estkme-group/rlpa-server) for eSTK.me Cloud Enhance function [easylpac]: https://github.com/creamlike1024/EasyLPAC/releases/latest [openeuicc]: https://gitea.angry.im/PeterCxy/OpenEUICC [openeuicc-mirror]: https://github.com/estkme-group/openeuicc ## Thanks [![Contributors][contrib]][contributors] [contrib]: https://contrib.rocks/image?repo=estkme-group/lpac [contributors]: https://github.com/estkme-group/lpac/graphs/contributors --- ## License See [REUSE.toml](REUSE.toml) and comment header of files for details. Copyright © 2023-2025 ESTKME TECHNOLOGY LIMITED, Hong Kong estkme-group-lpac-c2fcf5e/REUSE.toml000066400000000000000000000023711504765665400174350ustar00rootroot00000000000000version = 1 [[annotations]] path = [ "src/**", "driver/**", "utils/**", ] SPDX-FileCopyrightText = "2023-2025 ESTKME TECHNOLOGY LIMITED, Hong Kong" SPDX-License-Identifier = "AGPL-3.0-only" [[annotations]] path = ["euicc/**"] SPDX-FileCopyrightText = "2023-2025 ESTKME TECHNOLOGY LIMITED, Hong Kong" SPDX-License-Identifier = "LGPL-2.1-only OR LicenseRef-ESTKME-Commercial" [[annotations]] path = [ # Auxiliary files ".github/**", # Build system files "cmake/**", "CMakeLists.txt", # Documents "docs/**", "README.md", # Config for other tools ".editorconfig", ".clang-format", ".git-blame-ignore-revs", ".gitignore" ] SPDX-FileCopyrightText = "2023-2025 ESTKME TECHNOLOGY LIMITED, Hong Kong" SPDX-License-Identifier = "MIT" [[annotations]] path = ["cjson/**"] SPDX-FileCopyrightText = "2009-2017 Dave Gamble and cJSON contributors" SPDX-FileContributor = [ "ESTKME TECHNOLOGY LIMITED, Hong Kong", "Celeste Liu" ] SPDX-License-Identifier = "MIT" [[annotations]] path = ["dlfcn-win32/**"] SPDX-FileCopyrightText = "2007 Ramiro Polla" SPDX-FileContributor = [ "ESTKME TECHNOLOGY LIMITED, Hong Kong", "Celeste Liu", "IcedTangerine", "Yegor Yefremov" ] SPDX-License-Identifier = "MIT" estkme-group-lpac-c2fcf5e/cjson/000077500000000000000000000000001504765665400167665ustar00rootroot00000000000000estkme-group-lpac-c2fcf5e/cjson/CMakeLists.txt000066400000000000000000000003261504765665400215270ustar00rootroot00000000000000aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} LIB_CJSON_SRCS) add_library(cjson-static STATIC ${LIB_CJSON_SRCS}) target_include_directories(cjson-static PUBLIC $) estkme-group-lpac-c2fcf5e/cjson/LICENSE000066400000000000000000000020731504765665400177750ustar00rootroot00000000000000Copyright (c) 2009-2017 Dave Gamble and cJSON contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. estkme-group-lpac-c2fcf5e/cjson/cJSON.c000066400000000000000000002302071504765665400200520ustar00rootroot00000000000000/* Copyright (c) 2009-2017 Dave Gamble and cJSON contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* cJSON */ /* JSON parser in C. */ /* disable warnings about old C89 functions in MSVC */ #if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) #define _CRT_SECURE_NO_DEPRECATE #endif #ifdef __GNUC__ #pragma GCC visibility push(default) #endif #if defined(_MSC_VER) #pragma warning (push) /* disable warning about single line comments in system headers */ #pragma warning (disable : 4001) #endif #include #include #include #include #include #include #include #ifdef ENABLE_LOCALES #include #endif #if defined(_MSC_VER) #pragma warning (pop) #endif #ifdef __GNUC__ #pragma GCC visibility pop #endif #include "cJSON.h" /* define our own boolean type */ #ifdef true #undef true #endif #define true ((cJSON_bool)1) #ifdef false #undef false #endif #define false ((cJSON_bool)0) /* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ #ifndef isinf #define isinf(d) (isnan((d - d)) && !isnan(d)) #endif #ifndef isnan #define isnan(d) (d != d) #endif #ifndef NAN #ifdef _WIN32 #define NAN sqrt(-1.0) #else #define NAN 0.0/0.0 #endif #endif typedef struct { const unsigned char *json; size_t position; } error; static error global_error = { NULL, 0 }; CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) { return (const char*) (global_error.json + global_error.position); } CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) { if (!cJSON_IsString(item)) { return NULL; } return item->valuestring; } CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) { if (!cJSON_IsNumber(item)) { return (double) NAN; } return item->valuedouble; } /* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ #if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 16) #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. #endif CJSON_PUBLIC(const char*) cJSON_Version(void) { static char version[15]; sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); return version; } /* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) { if ((string1 == NULL) || (string2 == NULL)) { return 1; } if (string1 == string2) { return 0; } for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) { if (*string1 == '\0') { return 0; } } return tolower(*string1) - tolower(*string2); } typedef struct internal_hooks { void *(CJSON_CDECL *allocate)(size_t size); void (CJSON_CDECL *deallocate)(void *pointer); void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); } internal_hooks; #if defined(_MSC_VER) /* work around MSVC error C2322: '...' address of dllimport '...' is not static */ static void * CJSON_CDECL internal_malloc(size_t size) { return malloc(size); } static void CJSON_CDECL internal_free(void *pointer) { free(pointer); } static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) { return realloc(pointer, size); } #else #define internal_malloc malloc #define internal_free free #define internal_realloc realloc #endif /* strlen of character literals resolved at compile time */ #define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) { size_t length = 0; unsigned char *copy = NULL; if (string == NULL) { return NULL; } length = strlen((const char*)string) + sizeof(""); copy = (unsigned char*)hooks->allocate(length); if (copy == NULL) { return NULL; } memcpy(copy, string, length); return copy; } CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) { if (hooks == NULL) { /* Reset hooks */ global_hooks.allocate = malloc; global_hooks.deallocate = free; global_hooks.reallocate = realloc; return; } global_hooks.allocate = malloc; if (hooks->malloc_fn != NULL) { global_hooks.allocate = hooks->malloc_fn; } global_hooks.deallocate = free; if (hooks->free_fn != NULL) { global_hooks.deallocate = hooks->free_fn; } /* use realloc only if both free and malloc are used */ global_hooks.reallocate = NULL; if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) { global_hooks.reallocate = realloc; } } /* Internal constructor. */ static cJSON *cJSON_New_Item(const internal_hooks * const hooks) { cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); if (node) { memset(node, '\0', sizeof(cJSON)); } return node; } /* Delete a cJSON structure. */ CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) { cJSON *next = NULL; while (item != NULL) { next = item->next; if (!(item->type & cJSON_IsReference) && (item->child != NULL)) { cJSON_Delete(item->child); } if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) { global_hooks.deallocate(item->valuestring); } if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { global_hooks.deallocate(item->string); } global_hooks.deallocate(item); item = next; } } /* get the decimal point character of the current locale */ static unsigned char get_decimal_point(void) { #ifdef ENABLE_LOCALES struct lconv *lconv = localeconv(); return (unsigned char) lconv->decimal_point[0]; #else return '.'; #endif } typedef struct { const unsigned char *content; size_t length; size_t offset; size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ internal_hooks hooks; } parse_buffer; /* check if the given size is left to read in a given parse buffer (starting with 1) */ #define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) /* check if the buffer can be accessed at the given index (starting with 0) */ #define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) #define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) /* get a pointer to the buffer at the position */ #define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) /* Parse the input text to generate a number, and populate the result into item. */ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) { double number = 0; unsigned char *after_end = NULL; unsigned char number_c_string[64]; unsigned char decimal_point = get_decimal_point(); size_t i = 0; if ((input_buffer == NULL) || (input_buffer->content == NULL)) { return false; } /* copy the number into a temporary buffer and replace '.' with the decimal point * of the current locale (for strtod) * This also takes care of '\0' not necessarily being available for marking the end of the input */ for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) { switch (buffer_at_offset(input_buffer)[i]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '+': case '-': case 'e': case 'E': number_c_string[i] = buffer_at_offset(input_buffer)[i]; break; case '.': number_c_string[i] = decimal_point; break; default: goto loop_end; } } loop_end: number_c_string[i] = '\0'; number = strtod((const char*)number_c_string, (char**)&after_end); if (number_c_string == after_end) { return false; /* parse_error */ } item->valuedouble = number; /* use saturation in case of overflow */ if (number >= INT_MAX) { item->valueint = INT_MAX; } else if (number <= (double)INT_MIN) { item->valueint = INT_MIN; } else { item->valueint = (int)number; } item->type = cJSON_Number; input_buffer->offset += (size_t)(after_end - number_c_string); return true; } /* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) { if (number >= INT_MAX) { object->valueint = INT_MAX; } else if (number <= (double)INT_MIN) { object->valueint = INT_MIN; } else { object->valueint = (int)number; } return object->valuedouble = number; } CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) { char *copy = NULL; /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference)) { return NULL; } if (strlen(valuestring) <= strlen(object->valuestring)) { strcpy(object->valuestring, valuestring); return object->valuestring; } copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks); if (copy == NULL) { return NULL; } if (object->valuestring != NULL) { cJSON_free(object->valuestring); } object->valuestring = copy; return copy; } typedef struct { unsigned char *buffer; size_t length; size_t offset; size_t depth; /* current nesting depth (for formatted printing) */ cJSON_bool noalloc; cJSON_bool format; /* is this print a formatted print */ internal_hooks hooks; } printbuffer; /* realloc printbuffer if necessary to have at least "needed" bytes more */ static unsigned char* ensure(printbuffer * const p, size_t needed) { unsigned char *newbuffer = NULL; size_t newsize = 0; if ((p == NULL) || (p->buffer == NULL)) { return NULL; } if ((p->length > 0) && (p->offset >= p->length)) { /* make sure that offset is valid */ return NULL; } if (needed > INT_MAX) { /* sizes bigger than INT_MAX are currently not supported */ return NULL; } needed += p->offset + 1; if (needed <= p->length) { return p->buffer + p->offset; } if (p->noalloc) { return NULL; } /* calculate new buffer size */ if (needed > (INT_MAX / 2)) { /* overflow of int, use INT_MAX if possible */ if (needed <= INT_MAX) { newsize = INT_MAX; } else { return NULL; } } else { newsize = needed * 2; } if (p->hooks.reallocate != NULL) { /* reallocate with realloc if available */ newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); if (newbuffer == NULL) { p->hooks.deallocate(p->buffer); p->length = 0; p->buffer = NULL; return NULL; } } else { /* otherwise reallocate manually */ newbuffer = (unsigned char*)p->hooks.allocate(newsize); if (!newbuffer) { p->hooks.deallocate(p->buffer); p->length = 0; p->buffer = NULL; return NULL; } memcpy(newbuffer, p->buffer, p->offset + 1); p->hooks.deallocate(p->buffer); } p->length = newsize; p->buffer = newbuffer; return newbuffer + p->offset; } /* calculate the new length of the string in a printbuffer and update the offset */ static void update_offset(printbuffer * const buffer) { const unsigned char *buffer_pointer = NULL; if ((buffer == NULL) || (buffer->buffer == NULL)) { return; } buffer_pointer = buffer->buffer + buffer->offset; buffer->offset += strlen((const char*)buffer_pointer); } /* securely comparison of floating-point variables */ static cJSON_bool compare_double(double a, double b) { double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); return (fabs(a - b) <= maxVal * DBL_EPSILON); } /* Render the number nicely from the given item into a string. */ static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) { unsigned char *output_pointer = NULL; double d = item->valuedouble; int length = 0; size_t i = 0; unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ unsigned char decimal_point = get_decimal_point(); double test = 0.0; if (output_buffer == NULL) { return false; } /* This checks for NaN and Infinity */ if (isnan(d) || isinf(d)) { length = sprintf((char*)number_buffer, "null"); } else if(d == (double)item->valueint) { length = sprintf((char*)number_buffer, "%d", item->valueint); } else { /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ length = sprintf((char*)number_buffer, "%1.15g", d); /* Check whether the original double can be recovered */ if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) { /* If not, print with 17 decimal places of precision */ length = sprintf((char*)number_buffer, "%1.17g", d); } } /* sprintf failed or buffer overrun occurred */ if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) { return false; } /* reserve appropriate space in the output */ output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); if (output_pointer == NULL) { return false; } /* copy the printed number to the output and replace locale * dependent decimal point with '.' */ for (i = 0; i < ((size_t)length); i++) { if (number_buffer[i] == decimal_point) { output_pointer[i] = '.'; continue; } output_pointer[i] = number_buffer[i]; } output_pointer[i] = '\0'; output_buffer->offset += (size_t)length; return true; } /* parse 4 digit hexadecimal number */ static unsigned parse_hex4(const unsigned char * const input) { unsigned int h = 0; size_t i = 0; for (i = 0; i < 4; i++) { /* parse digit */ if ((input[i] >= '0') && (input[i] <= '9')) { h += (unsigned int) input[i] - '0'; } else if ((input[i] >= 'A') && (input[i] <= 'F')) { h += (unsigned int) 10 + input[i] - 'A'; } else if ((input[i] >= 'a') && (input[i] <= 'f')) { h += (unsigned int) 10 + input[i] - 'a'; } else /* invalid */ { return 0; } if (i < 3) { /* shift left to make place for the next nibble */ h = h << 4; } } return h; } /* converts a UTF-16 literal to UTF-8 * A literal can be one or two sequences of the form \uXXXX */ static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) { long unsigned int codepoint = 0; unsigned int first_code = 0; const unsigned char *first_sequence = input_pointer; unsigned char utf8_length = 0; unsigned char utf8_position = 0; unsigned char sequence_length = 0; unsigned char first_byte_mark = 0; if ((input_end - first_sequence) < 6) { /* input ends unexpectedly */ goto fail; } /* get the first utf16 sequence */ first_code = parse_hex4(first_sequence + 2); /* check that the code is valid */ if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) { goto fail; } /* UTF16 surrogate pair */ if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) { const unsigned char *second_sequence = first_sequence + 6; unsigned int second_code = 0; sequence_length = 12; /* \uXXXX\uXXXX */ if ((input_end - second_sequence) < 6) { /* input ends unexpectedly */ goto fail; } if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) { /* missing second half of the surrogate pair */ goto fail; } /* get the second utf16 sequence */ second_code = parse_hex4(second_sequence + 2); /* check that the code is valid */ if ((second_code < 0xDC00) || (second_code > 0xDFFF)) { /* invalid second half of the surrogate pair */ goto fail; } /* calculate the unicode codepoint from the surrogate pair */ codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); } else { sequence_length = 6; /* \uXXXX */ codepoint = first_code; } /* encode as UTF-8 * takes at maximum 4 bytes to encode: * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ if (codepoint < 0x80) { /* normal ascii, encoding 0xxxxxxx */ utf8_length = 1; } else if (codepoint < 0x800) { /* two bytes, encoding 110xxxxx 10xxxxxx */ utf8_length = 2; first_byte_mark = 0xC0; /* 11000000 */ } else if (codepoint < 0x10000) { /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ utf8_length = 3; first_byte_mark = 0xE0; /* 11100000 */ } else if (codepoint <= 0x10FFFF) { /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ utf8_length = 4; first_byte_mark = 0xF0; /* 11110000 */ } else { /* invalid unicode codepoint */ goto fail; } /* encode as utf8 */ for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) { /* 10xxxxxx */ (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); codepoint >>= 6; } /* encode first byte */ if (utf8_length > 1) { (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); } else { (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); } *output_pointer += utf8_length; return sequence_length; fail: return 0; } /* Parse the input text into an unescaped cinput, and populate item. */ static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) { const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; unsigned char *output_pointer = NULL; unsigned char *output = NULL; /* not a string */ if (buffer_at_offset(input_buffer)[0] != '\"') { goto fail; } { /* calculate approximate size of the output (overestimate) */ size_t allocation_length = 0; size_t skipped_bytes = 0; while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) { /* is escape sequence */ if (input_end[0] == '\\') { if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) { /* prevent buffer overflow when last input character is a backslash */ goto fail; } skipped_bytes++; input_end++; } input_end++; } if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) { goto fail; /* string ended unexpectedly */ } /* This is at most how much we need for the output */ allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); if (output == NULL) { goto fail; /* allocation failure */ } } output_pointer = output; /* loop through the string literal */ while (input_pointer < input_end) { if (*input_pointer != '\\') { *output_pointer++ = *input_pointer++; } /* escape sequence */ else { unsigned char sequence_length = 2; if ((input_end - input_pointer) < 1) { goto fail; } switch (input_pointer[1]) { case 'b': *output_pointer++ = '\b'; break; case 'f': *output_pointer++ = '\f'; break; case 'n': *output_pointer++ = '\n'; break; case 'r': *output_pointer++ = '\r'; break; case 't': *output_pointer++ = '\t'; break; case '\"': case '\\': case '/': *output_pointer++ = input_pointer[1]; break; /* UTF-16 literal */ case 'u': sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); if (sequence_length == 0) { /* failed to convert UTF16-literal to UTF-8 */ goto fail; } break; default: goto fail; } input_pointer += sequence_length; } } /* zero terminate the output */ *output_pointer = '\0'; item->type = cJSON_String; item->valuestring = (char*)output; input_buffer->offset = (size_t) (input_end - input_buffer->content); input_buffer->offset++; return true; fail: if (output != NULL) { input_buffer->hooks.deallocate(output); } if (input_pointer != NULL) { input_buffer->offset = (size_t)(input_pointer - input_buffer->content); } return false; } /* Render the cstring provided to an escaped version that can be printed. */ static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) { const unsigned char *input_pointer = NULL; unsigned char *output = NULL; unsigned char *output_pointer = NULL; size_t output_length = 0; /* numbers of additional characters needed for escaping */ size_t escape_characters = 0; if (output_buffer == NULL) { return false; } /* empty string */ if (input == NULL) { output = ensure(output_buffer, sizeof("\"\"")); if (output == NULL) { return false; } strcpy((char*)output, "\"\""); return true; } /* set "flag" to 1 if something needs to be escaped */ for (input_pointer = input; *input_pointer; input_pointer++) { switch (*input_pointer) { case '\"': case '\\': case '\b': case '\f': case '\n': case '\r': case '\t': /* one character escape sequence */ escape_characters++; break; default: if (*input_pointer < 32) { /* UTF-16 escape sequence uXXXX */ escape_characters += 5; } break; } } output_length = (size_t)(input_pointer - input) + escape_characters; output = ensure(output_buffer, output_length + sizeof("\"\"")); if (output == NULL) { return false; } /* no characters have to be escaped */ if (escape_characters == 0) { output[0] = '\"'; memcpy(output + 1, input, output_length); output[output_length + 1] = '\"'; output[output_length + 2] = '\0'; return true; } output[0] = '\"'; output_pointer = output + 1; /* copy the string */ for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) { if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) { /* normal character, copy */ *output_pointer = *input_pointer; } else { /* character needs to be escaped */ *output_pointer++ = '\\'; switch (*input_pointer) { case '\\': *output_pointer = '\\'; break; case '\"': *output_pointer = '\"'; break; case '\b': *output_pointer = 'b'; break; case '\f': *output_pointer = 'f'; break; case '\n': *output_pointer = 'n'; break; case '\r': *output_pointer = 'r'; break; case '\t': *output_pointer = 't'; break; default: /* escape and print as unicode codepoint */ sprintf((char*)output_pointer, "u%04x", *input_pointer); output_pointer += 4; break; } } } output[output_length + 1] = '\"'; output[output_length + 2] = '\0'; return true; } /* Invoke print_string_ptr (which is useful) on an item. */ static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) { return print_string_ptr((unsigned char*)item->valuestring, p); } /* Predeclare these prototypes. */ static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); /* Utility to jump whitespace and cr/lf */ static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) { if ((buffer == NULL) || (buffer->content == NULL)) { return NULL; } if (cannot_access_at_index(buffer, 0)) { return buffer; } while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) { buffer->offset++; } if (buffer->offset == buffer->length) { buffer->offset--; } return buffer; } /* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) { if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) { return NULL; } if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) { buffer->offset += 3; } return buffer; } CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) { size_t buffer_length; if (NULL == value) { return NULL; } /* Adding null character size due to require_null_terminated. */ buffer_length = strlen(value) + sizeof(""); return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); } /* Parse an object - create a new root, and populate. */ CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) { parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; cJSON *item = NULL; /* reset error position */ global_error.json = NULL; global_error.position = 0; if (value == NULL || 0 == buffer_length) { goto fail; } buffer.content = (const unsigned char*)value; buffer.length = buffer_length; buffer.offset = 0; buffer.hooks = global_hooks; item = cJSON_New_Item(&global_hooks); if (item == NULL) /* memory fail */ { goto fail; } if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) { /* parse failure. ep is set. */ goto fail; } /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ if (require_null_terminated) { buffer_skip_whitespace(&buffer); if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') { goto fail; } } if (return_parse_end) { *return_parse_end = (const char*)buffer_at_offset(&buffer); } return item; fail: if (item != NULL) { cJSON_Delete(item); } if (value != NULL) { error local_error; local_error.json = (const unsigned char*)value; local_error.position = 0; if (buffer.offset < buffer.length) { local_error.position = buffer.offset; } else if (buffer.length > 0) { local_error.position = buffer.length - 1; } if (return_parse_end != NULL) { *return_parse_end = (const char*)local_error.json + local_error.position; } global_error = local_error; } return NULL; } /* Default options for cJSON_Parse */ CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) { return cJSON_ParseWithOpts(value, 0, 0); } CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) { return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); } #define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) { static const size_t default_buffer_size = 256; printbuffer buffer[1]; unsigned char *printed = NULL; memset(buffer, 0, sizeof(buffer)); /* create buffer */ buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); buffer->length = default_buffer_size; buffer->format = format; buffer->hooks = *hooks; if (buffer->buffer == NULL) { goto fail; } /* print the value */ if (!print_value(item, buffer)) { goto fail; } update_offset(buffer); /* check if reallocate is available */ if (hooks->reallocate != NULL) { printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); if (printed == NULL) { goto fail; } buffer->buffer = NULL; } else /* otherwise copy the JSON over to a new buffer */ { printed = (unsigned char*) hooks->allocate(buffer->offset + 1); if (printed == NULL) { goto fail; } memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); printed[buffer->offset] = '\0'; /* just to be sure */ /* free the buffer */ hooks->deallocate(buffer->buffer); } return printed; fail: if (buffer->buffer != NULL) { hooks->deallocate(buffer->buffer); } if (printed != NULL) { hooks->deallocate(printed); } return NULL; } /* Render a cJSON item/entity/structure to text. */ CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) { return (char*)print(item, true, &global_hooks); } CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) { return (char*)print(item, false, &global_hooks); } CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) { printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; if (prebuffer < 0) { return NULL; } p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); if (!p.buffer) { return NULL; } p.length = (size_t)prebuffer; p.offset = 0; p.noalloc = false; p.format = fmt; p.hooks = global_hooks; if (!print_value(item, &p)) { global_hooks.deallocate(p.buffer); return NULL; } return (char*)p.buffer; } CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) { printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; if ((length < 0) || (buffer == NULL)) { return false; } p.buffer = (unsigned char*)buffer; p.length = (size_t)length; p.offset = 0; p.noalloc = true; p.format = format; p.hooks = global_hooks; return print_value(item, &p); } /* Parser core - when encountering text, process appropriately. */ static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) { if ((input_buffer == NULL) || (input_buffer->content == NULL)) { return false; /* no input */ } /* parse the different types of values */ /* null */ if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) { item->type = cJSON_NULL; input_buffer->offset += 4; return true; } /* false */ if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) { item->type = cJSON_False; input_buffer->offset += 5; return true; } /* true */ if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) { item->type = cJSON_True; item->valueint = 1; input_buffer->offset += 4; return true; } /* string */ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) { return parse_string(item, input_buffer); } /* number */ if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) { return parse_number(item, input_buffer); } /* array */ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) { return parse_array(item, input_buffer); } /* object */ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) { return parse_object(item, input_buffer); } return false; } /* Render a value to text. */ static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) { unsigned char *output = NULL; if ((item == NULL) || (output_buffer == NULL)) { return false; } switch ((item->type) & 0xFF) { case cJSON_NULL: output = ensure(output_buffer, 5); if (output == NULL) { return false; } strcpy((char*)output, "null"); return true; case cJSON_False: output = ensure(output_buffer, 6); if (output == NULL) { return false; } strcpy((char*)output, "false"); return true; case cJSON_True: output = ensure(output_buffer, 5); if (output == NULL) { return false; } strcpy((char*)output, "true"); return true; case cJSON_Number: return print_number(item, output_buffer); case cJSON_Raw: { size_t raw_length = 0; if (item->valuestring == NULL) { return false; } raw_length = strlen(item->valuestring) + sizeof(""); output = ensure(output_buffer, raw_length); if (output == NULL) { return false; } memcpy(output, item->valuestring, raw_length); return true; } case cJSON_String: return print_string(item, output_buffer); case cJSON_Array: return print_array(item, output_buffer); case cJSON_Object: return print_object(item, output_buffer); default: return false; } } /* Build an array from input text. */ static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) { cJSON *head = NULL; /* head of the linked list */ cJSON *current_item = NULL; if (input_buffer->depth >= CJSON_NESTING_LIMIT) { return false; /* to deeply nested */ } input_buffer->depth++; if (buffer_at_offset(input_buffer)[0] != '[') { /* not an array */ goto fail; } input_buffer->offset++; buffer_skip_whitespace(input_buffer); if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) { /* empty array */ goto success; } /* check if we skipped to the end of the buffer */ if (cannot_access_at_index(input_buffer, 0)) { input_buffer->offset--; goto fail; } /* step back to character in front of the first element */ input_buffer->offset--; /* loop through the comma separated array elements */ do { /* allocate next item */ cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); if (new_item == NULL) { goto fail; /* allocation failure */ } /* attach next item to list */ if (head == NULL) { /* start the linked list */ current_item = head = new_item; } else { /* add to the end and advance */ current_item->next = new_item; new_item->prev = current_item; current_item = new_item; } /* parse next value */ input_buffer->offset++; buffer_skip_whitespace(input_buffer); if (!parse_value(current_item, input_buffer)) { goto fail; /* failed to parse value */ } buffer_skip_whitespace(input_buffer); } while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') { goto fail; /* expected end of array */ } success: input_buffer->depth--; if (head != NULL) { head->prev = current_item; } item->type = cJSON_Array; item->child = head; input_buffer->offset++; return true; fail: if (head != NULL) { cJSON_Delete(head); } return false; } /* Render an array to text */ static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) { unsigned char *output_pointer = NULL; size_t length = 0; cJSON *current_element = item->child; if (output_buffer == NULL) { return false; } /* Compose the output array. */ /* opening square bracket */ output_pointer = ensure(output_buffer, 1); if (output_pointer == NULL) { return false; } *output_pointer = '['; output_buffer->offset++; output_buffer->depth++; while (current_element != NULL) { if (!print_value(current_element, output_buffer)) { return false; } update_offset(output_buffer); if (current_element->next) { length = (size_t) (output_buffer->format ? 2 : 1); output_pointer = ensure(output_buffer, length + 1); if (output_pointer == NULL) { return false; } *output_pointer++ = ','; if(output_buffer->format) { *output_pointer++ = ' '; } *output_pointer = '\0'; output_buffer->offset += length; } current_element = current_element->next; } output_pointer = ensure(output_buffer, 2); if (output_pointer == NULL) { return false; } *output_pointer++ = ']'; *output_pointer = '\0'; output_buffer->depth--; return true; } /* Build an object from the text. */ static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) { cJSON *head = NULL; /* linked list head */ cJSON *current_item = NULL; if (input_buffer->depth >= CJSON_NESTING_LIMIT) { return false; /* to deeply nested */ } input_buffer->depth++; if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) { goto fail; /* not an object */ } input_buffer->offset++; buffer_skip_whitespace(input_buffer); if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) { goto success; /* empty object */ } /* check if we skipped to the end of the buffer */ if (cannot_access_at_index(input_buffer, 0)) { input_buffer->offset--; goto fail; } /* step back to character in front of the first element */ input_buffer->offset--; /* loop through the comma separated array elements */ do { /* allocate next item */ cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); if (new_item == NULL) { goto fail; /* allocation failure */ } /* attach next item to list */ if (head == NULL) { /* start the linked list */ current_item = head = new_item; } else { /* add to the end and advance */ current_item->next = new_item; new_item->prev = current_item; current_item = new_item; } /* parse the name of the child */ input_buffer->offset++; buffer_skip_whitespace(input_buffer); if (!parse_string(current_item, input_buffer)) { goto fail; /* failed to parse name */ } buffer_skip_whitespace(input_buffer); /* swap valuestring and string, because we parsed the name */ current_item->string = current_item->valuestring; current_item->valuestring = NULL; if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) { goto fail; /* invalid object */ } /* parse the value */ input_buffer->offset++; buffer_skip_whitespace(input_buffer); if (!parse_value(current_item, input_buffer)) { goto fail; /* failed to parse value */ } buffer_skip_whitespace(input_buffer); } while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) { goto fail; /* expected end of object */ } success: input_buffer->depth--; if (head != NULL) { head->prev = current_item; } item->type = cJSON_Object; item->child = head; input_buffer->offset++; return true; fail: if (head != NULL) { cJSON_Delete(head); } return false; } /* Render an object to text. */ static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) { unsigned char *output_pointer = NULL; size_t length = 0; cJSON *current_item = item->child; if (output_buffer == NULL) { return false; } /* Compose the output: */ length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ output_pointer = ensure(output_buffer, length + 1); if (output_pointer == NULL) { return false; } *output_pointer++ = '{'; output_buffer->depth++; if (output_buffer->format) { *output_pointer++ = '\n'; } output_buffer->offset += length; while (current_item) { if (output_buffer->format) { size_t i; output_pointer = ensure(output_buffer, output_buffer->depth); if (output_pointer == NULL) { return false; } for (i = 0; i < output_buffer->depth; i++) { *output_pointer++ = '\t'; } output_buffer->offset += output_buffer->depth; } /* print key */ if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) { return false; } update_offset(output_buffer); length = (size_t) (output_buffer->format ? 2 : 1); output_pointer = ensure(output_buffer, length); if (output_pointer == NULL) { return false; } *output_pointer++ = ':'; if (output_buffer->format) { *output_pointer++ = '\t'; } output_buffer->offset += length; /* print value */ if (!print_value(current_item, output_buffer)) { return false; } update_offset(output_buffer); /* print comma if not last */ length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); output_pointer = ensure(output_buffer, length + 1); if (output_pointer == NULL) { return false; } if (current_item->next) { *output_pointer++ = ','; } if (output_buffer->format) { *output_pointer++ = '\n'; } *output_pointer = '\0'; output_buffer->offset += length; current_item = current_item->next; } output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); if (output_pointer == NULL) { return false; } if (output_buffer->format) { size_t i; for (i = 0; i < (output_buffer->depth - 1); i++) { *output_pointer++ = '\t'; } } *output_pointer++ = '}'; *output_pointer = '\0'; output_buffer->depth--; return true; } /* Get Array size/item / object item. */ CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) { cJSON *child = NULL; size_t size = 0; if (array == NULL) { return 0; } child = array->child; while(child != NULL) { size++; child = child->next; } /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ return (int)size; } static cJSON* get_array_item(const cJSON *array, size_t index) { cJSON *current_child = NULL; if (array == NULL) { return NULL; } current_child = array->child; while ((current_child != NULL) && (index > 0)) { index--; current_child = current_child->next; } return current_child; } CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) { if (index < 0) { return NULL; } return get_array_item(array, (size_t)index); } static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) { cJSON *current_element = NULL; if ((object == NULL) || (name == NULL)) { return NULL; } current_element = object->child; if (case_sensitive) { while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) { current_element = current_element->next; } } else { while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) { current_element = current_element->next; } } if ((current_element == NULL) || (current_element->string == NULL)) { return NULL; } return current_element; } CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) { return get_object_item(object, string, false); } CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) { return get_object_item(object, string, true); } CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) { return cJSON_GetObjectItem(object, string) ? 1 : 0; } /* Utility for array list handling. */ static void suffix_object(cJSON *prev, cJSON *item) { prev->next = item; item->prev = prev; } /* Utility for handling references. */ static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) { cJSON *reference = NULL; if (item == NULL) { return NULL; } reference = cJSON_New_Item(hooks); if (reference == NULL) { return NULL; } memcpy(reference, item, sizeof(cJSON)); reference->string = NULL; reference->type |= cJSON_IsReference; reference->next = reference->prev = NULL; return reference; } static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) { cJSON *child = NULL; if ((item == NULL) || (array == NULL) || (array == item)) { return false; } child = array->child; /* * To find the last item in array quickly, we use prev in array */ if (child == NULL) { /* list is empty, start new one */ array->child = item; item->prev = item; item->next = NULL; } else { /* append to the end */ if (child->prev) { suffix_object(child->prev, item); array->child->prev = item; } } return true; } /* Add item to array/object. */ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) { return add_item_to_array(array, item); } #if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) #pragma GCC diagnostic push #endif #ifdef __GNUC__ #pragma GCC diagnostic ignored "-Wcast-qual" #endif /* helper function to cast away const */ static void* cast_away_const(const void* string) { return (void*)string; } #if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) #pragma GCC diagnostic pop #endif static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) { char *new_key = NULL; int new_type = cJSON_Invalid; if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) { return false; } if (constant_key) { new_key = (char*)cast_away_const(string); new_type = item->type | cJSON_StringIsConst; } else { new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); if (new_key == NULL) { return false; } new_type = item->type & ~cJSON_StringIsConst; } if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { hooks->deallocate(item->string); } item->string = new_key; item->type = new_type; return add_item_to_array(object, item); } CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) { return add_item_to_object(object, string, item, &global_hooks, false); } /* Add an item to an object with constant string as key */ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) { return add_item_to_object(object, string, item, &global_hooks, true); } CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) { if (array == NULL) { return false; } return add_item_to_array(array, create_reference(item, &global_hooks)); } CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) { if ((object == NULL) || (string == NULL)) { return false; } return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); } CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) { cJSON *null = cJSON_CreateNull(); if (add_item_to_object(object, name, null, &global_hooks, false)) { return null; } cJSON_Delete(null); return NULL; } CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) { cJSON *true_item = cJSON_CreateTrue(); if (add_item_to_object(object, name, true_item, &global_hooks, false)) { return true_item; } cJSON_Delete(true_item); return NULL; } CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) { cJSON *false_item = cJSON_CreateFalse(); if (add_item_to_object(object, name, false_item, &global_hooks, false)) { return false_item; } cJSON_Delete(false_item); return NULL; } CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) { cJSON *bool_item = cJSON_CreateBool(boolean); if (add_item_to_object(object, name, bool_item, &global_hooks, false)) { return bool_item; } cJSON_Delete(bool_item); return NULL; } CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) { cJSON *number_item = cJSON_CreateNumber(number); if (add_item_to_object(object, name, number_item, &global_hooks, false)) { return number_item; } cJSON_Delete(number_item); return NULL; } CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) { cJSON *string_item = cJSON_CreateString(string); if (add_item_to_object(object, name, string_item, &global_hooks, false)) { return string_item; } cJSON_Delete(string_item); return NULL; } CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) { cJSON *raw_item = cJSON_CreateRaw(raw); if (add_item_to_object(object, name, raw_item, &global_hooks, false)) { return raw_item; } cJSON_Delete(raw_item); return NULL; } CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) { cJSON *object_item = cJSON_CreateObject(); if (add_item_to_object(object, name, object_item, &global_hooks, false)) { return object_item; } cJSON_Delete(object_item); return NULL; } CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) { cJSON *array = cJSON_CreateArray(); if (add_item_to_object(object, name, array, &global_hooks, false)) { return array; } cJSON_Delete(array); return NULL; } CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) { if ((parent == NULL) || (item == NULL)) { return NULL; } if (item != parent->child) { /* not the first element */ item->prev->next = item->next; } if (item->next != NULL) { /* not the last element */ item->next->prev = item->prev; } if (item == parent->child) { /* first element */ parent->child = item->next; } else if (item->next == NULL) { /* last element */ parent->child->prev = item->prev; } /* make sure the detached item doesn't point anywhere anymore */ item->prev = NULL; item->next = NULL; return item; } CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) { if (which < 0) { return NULL; } return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); } CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) { cJSON_Delete(cJSON_DetachItemFromArray(array, which)); } CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) { cJSON *to_detach = cJSON_GetObjectItem(object, string); return cJSON_DetachItemViaPointer(object, to_detach); } CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) { cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); return cJSON_DetachItemViaPointer(object, to_detach); } CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) { cJSON_Delete(cJSON_DetachItemFromObject(object, string)); } CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) { cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); } /* Replace array/object items with new ones. */ CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) { cJSON *after_inserted = NULL; if (which < 0) { return false; } after_inserted = get_array_item(array, (size_t)which); if (after_inserted == NULL) { return add_item_to_array(array, newitem); } newitem->next = after_inserted; newitem->prev = after_inserted->prev; after_inserted->prev = newitem; if (after_inserted == array->child) { array->child = newitem; } else { newitem->prev->next = newitem; } return true; } CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) { if ((parent == NULL) || (parent->child == NULL) || (replacement == NULL) || (item == NULL)) { return false; } if (replacement == item) { return true; } replacement->next = item->next; replacement->prev = item->prev; if (replacement->next != NULL) { replacement->next->prev = replacement; } if (parent->child == item) { if (parent->child->prev == parent->child) { replacement->prev = replacement; } parent->child = replacement; } else { /* * To find the last item in array quickly, we use prev in array. * We can't modify the last item's next pointer where this item was the parent's child */ if (replacement->prev != NULL) { replacement->prev->next = replacement; } if (replacement->next == NULL) { parent->child->prev = replacement; } } item->next = NULL; item->prev = NULL; cJSON_Delete(item); return true; } CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) { if (which < 0) { return false; } return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); } static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) { if ((replacement == NULL) || (string == NULL)) { return false; } /* replace the name in the replacement */ if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) { cJSON_free(replacement->string); } replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); if (replacement->string == NULL) { return false; } replacement->type &= ~cJSON_StringIsConst; return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); } CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) { return replace_item_in_object(object, string, newitem, false); } CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) { return replace_item_in_object(object, string, newitem, true); } /* Create basic types: */ CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) { cJSON *item = cJSON_New_Item(&global_hooks); if(item) { item->type = cJSON_NULL; } return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) { cJSON *item = cJSON_New_Item(&global_hooks); if(item) { item->type = cJSON_True; } return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) { cJSON *item = cJSON_New_Item(&global_hooks); if(item) { item->type = cJSON_False; } return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) { cJSON *item = cJSON_New_Item(&global_hooks); if(item) { item->type = boolean ? cJSON_True : cJSON_False; } return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) { cJSON *item = cJSON_New_Item(&global_hooks); if(item) { item->type = cJSON_Number; item->valuedouble = num; /* use saturation in case of overflow */ if (num >= INT_MAX) { item->valueint = INT_MAX; } else if (num <= (double)INT_MIN) { item->valueint = INT_MIN; } else { item->valueint = (int)num; } } return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) { cJSON *item = cJSON_New_Item(&global_hooks); if(item) { item->type = cJSON_String; item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); if(!item->valuestring) { cJSON_Delete(item); return NULL; } } return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) { cJSON *item = cJSON_New_Item(&global_hooks); if (item != NULL) { item->type = cJSON_String | cJSON_IsReference; item->valuestring = (char*)cast_away_const(string); } return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) { cJSON *item = cJSON_New_Item(&global_hooks); if (item != NULL) { item->type = cJSON_Object | cJSON_IsReference; item->child = (cJSON*)cast_away_const(child); } return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { cJSON *item = cJSON_New_Item(&global_hooks); if (item != NULL) { item->type = cJSON_Array | cJSON_IsReference; item->child = (cJSON*)cast_away_const(child); } return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) { cJSON *item = cJSON_New_Item(&global_hooks); if(item) { item->type = cJSON_Raw; item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); if(!item->valuestring) { cJSON_Delete(item); return NULL; } } return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) { cJSON *item = cJSON_New_Item(&global_hooks); if(item) { item->type=cJSON_Array; } return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) { cJSON *item = cJSON_New_Item(&global_hooks); if (item) { item->type = cJSON_Object; } return item; } /* Create Arrays: */ CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) { size_t i = 0; cJSON *n = NULL; cJSON *p = NULL; cJSON *a = NULL; if ((count < 0) || (numbers == NULL)) { return NULL; } a = cJSON_CreateArray(); for(i = 0; a && (i < (size_t)count); i++) { n = cJSON_CreateNumber(numbers[i]); if (!n) { cJSON_Delete(a); return NULL; } if(!i) { a->child = n; } else { suffix_object(p, n); } p = n; } if (a && a->child) { a->child->prev = n; } return a; } CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) { size_t i = 0; cJSON *n = NULL; cJSON *p = NULL; cJSON *a = NULL; if ((count < 0) || (numbers == NULL)) { return NULL; } a = cJSON_CreateArray(); for(i = 0; a && (i < (size_t)count); i++) { n = cJSON_CreateNumber((double)numbers[i]); if(!n) { cJSON_Delete(a); return NULL; } if(!i) { a->child = n; } else { suffix_object(p, n); } p = n; } if (a && a->child) { a->child->prev = n; } return a; } CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) { size_t i = 0; cJSON *n = NULL; cJSON *p = NULL; cJSON *a = NULL; if ((count < 0) || (numbers == NULL)) { return NULL; } a = cJSON_CreateArray(); for(i = 0; a && (i < (size_t)count); i++) { n = cJSON_CreateNumber(numbers[i]); if(!n) { cJSON_Delete(a); return NULL; } if(!i) { a->child = n; } else { suffix_object(p, n); } p = n; } if (a && a->child) { a->child->prev = n; } return a; } CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) { size_t i = 0; cJSON *n = NULL; cJSON *p = NULL; cJSON *a = NULL; if ((count < 0) || (strings == NULL)) { return NULL; } a = cJSON_CreateArray(); for (i = 0; a && (i < (size_t)count); i++) { n = cJSON_CreateString(strings[i]); if(!n) { cJSON_Delete(a); return NULL; } if(!i) { a->child = n; } else { suffix_object(p,n); } p = n; } if (a && a->child) { a->child->prev = n; } return a; } /* Duplication */ CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) { cJSON *newitem = NULL; cJSON *child = NULL; cJSON *next = NULL; cJSON *newchild = NULL; /* Bail on bad ptr */ if (!item) { goto fail; } /* Create new item */ newitem = cJSON_New_Item(&global_hooks); if (!newitem) { goto fail; } /* Copy over all vars */ newitem->type = item->type & (~cJSON_IsReference); newitem->valueint = item->valueint; newitem->valuedouble = item->valuedouble; if (item->valuestring) { newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); if (!newitem->valuestring) { goto fail; } } if (item->string) { newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); if (!newitem->string) { goto fail; } } /* If non-recursive, then we're done! */ if (!recurse) { return newitem; } /* Walk the ->next chain for the child. */ child = item->child; while (child != NULL) { newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ if (!newchild) { goto fail; } if (next != NULL) { /* If newitem->child already set, then crosswire ->prev and ->next and move on */ next->next = newchild; newchild->prev = next; next = newchild; } else { /* Set newitem->child and move to it */ newitem->child = newchild; next = newchild; } child = child->next; } if (newitem && newitem->child) { newitem->child->prev = newchild; } return newitem; fail: if (newitem != NULL) { cJSON_Delete(newitem); } return NULL; } static void skip_oneline_comment(char **input) { *input += static_strlen("//"); for (; (*input)[0] != '\0'; ++(*input)) { if ((*input)[0] == '\n') { *input += static_strlen("\n"); return; } } } static void skip_multiline_comment(char **input) { *input += static_strlen("/*"); for (; (*input)[0] != '\0'; ++(*input)) { if (((*input)[0] == '*') && ((*input)[1] == '/')) { *input += static_strlen("*/"); return; } } } static void minify_string(char **input, char **output) { (*output)[0] = (*input)[0]; *input += static_strlen("\""); *output += static_strlen("\""); for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { (*output)[0] = (*input)[0]; if ((*input)[0] == '\"') { (*output)[0] = '\"'; *input += static_strlen("\""); *output += static_strlen("\""); return; } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { (*output)[1] = (*input)[1]; *input += static_strlen("\""); *output += static_strlen("\""); } } } CJSON_PUBLIC(void) cJSON_Minify(char *json) { char *into = json; if (json == NULL) { return; } while (json[0] != '\0') { switch (json[0]) { case ' ': case '\t': case '\r': case '\n': json++; break; case '/': if (json[1] == '/') { skip_oneline_comment(&json); } else if (json[1] == '*') { skip_multiline_comment(&json); } else { json++; } break; case '\"': minify_string(&json, (char**)&into); break; default: into[0] = json[0]; json++; into++; } } /* and null-terminate. */ *into = '\0'; } CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) { if (item == NULL) { return false; } return (item->type & 0xFF) == cJSON_Invalid; } CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) { if (item == NULL) { return false; } return (item->type & 0xFF) == cJSON_False; } CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) { if (item == NULL) { return false; } return (item->type & 0xff) == cJSON_True; } CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) { if (item == NULL) { return false; } return (item->type & (cJSON_True | cJSON_False)) != 0; } CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) { if (item == NULL) { return false; } return (item->type & 0xFF) == cJSON_NULL; } CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) { if (item == NULL) { return false; } return (item->type & 0xFF) == cJSON_Number; } CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) { if (item == NULL) { return false; } return (item->type & 0xFF) == cJSON_String; } CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) { if (item == NULL) { return false; } return (item->type & 0xFF) == cJSON_Array; } CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) { if (item == NULL) { return false; } return (item->type & 0xFF) == cJSON_Object; } CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) { if (item == NULL) { return false; } return (item->type & 0xFF) == cJSON_Raw; } CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) { if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) { return false; } /* check if type is valid */ switch (a->type & 0xFF) { case cJSON_False: case cJSON_True: case cJSON_NULL: case cJSON_Number: case cJSON_String: case cJSON_Raw: case cJSON_Array: case cJSON_Object: break; default: return false; } /* identical objects are equal */ if (a == b) { return true; } switch (a->type & 0xFF) { /* in these cases and equal type is enough */ case cJSON_False: case cJSON_True: case cJSON_NULL: return true; case cJSON_Number: if (compare_double(a->valuedouble, b->valuedouble)) { return true; } return false; case cJSON_String: case cJSON_Raw: if ((a->valuestring == NULL) || (b->valuestring == NULL)) { return false; } if (strcmp(a->valuestring, b->valuestring) == 0) { return true; } return false; case cJSON_Array: { cJSON *a_element = a->child; cJSON *b_element = b->child; for (; (a_element != NULL) && (b_element != NULL);) { if (!cJSON_Compare(a_element, b_element, case_sensitive)) { return false; } a_element = a_element->next; b_element = b_element->next; } /* one of the arrays is longer than the other */ if (a_element != b_element) { return false; } return true; } case cJSON_Object: { cJSON *a_element = NULL; cJSON *b_element = NULL; cJSON_ArrayForEach(a_element, a) { /* TODO This has O(n^2) runtime, which is horrible! */ b_element = get_object_item(b, a_element->string, case_sensitive); if (b_element == NULL) { return false; } if (!cJSON_Compare(a_element, b_element, case_sensitive)) { return false; } } /* doing this twice, once on a and b to prevent true comparison if a subset of b * TODO: Do this the proper way, this is just a fix for now */ cJSON_ArrayForEach(b_element, b) { a_element = get_object_item(a, b_element->string, case_sensitive); if (a_element == NULL) { return false; } if (!cJSON_Compare(b_element, a_element, case_sensitive)) { return false; } } return true; } default: return false; } } CJSON_PUBLIC(void *) cJSON_malloc(size_t size) { return global_hooks.allocate(size); } CJSON_PUBLIC(void) cJSON_free(void *object) { global_hooks.deallocate(object); } estkme-group-lpac-c2fcf5e/cjson/cJSON.h000066400000000000000000000375011504765665400200610ustar00rootroot00000000000000/* Copyright (c) 2009-2017 Dave Gamble and cJSON contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef cJSON__h #define cJSON__h #ifdef __cplusplus extern "C" { #endif #if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) #define __WINDOWS__ #endif #ifdef __WINDOWS__ /* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol For *nix builds that support visibility attribute, you can define similar behavior by setting default visibility to hidden by adding -fvisibility=hidden (for gcc) or -xldscope=hidden (for sun cc) to CFLAGS then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does */ #define CJSON_CDECL __cdecl #define CJSON_STDCALL __stdcall /* export symbols by default, this is necessary for copy pasting the C and header file */ #if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) #define CJSON_EXPORT_SYMBOLS #endif #if defined(CJSON_HIDE_SYMBOLS) #define CJSON_PUBLIC(type) type CJSON_STDCALL #elif defined(CJSON_EXPORT_SYMBOLS) #define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL #elif defined(CJSON_IMPORT_SYMBOLS) #define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL #endif #else /* !__WINDOWS__ */ #define CJSON_CDECL #define CJSON_STDCALL #if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) #define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type #else #define CJSON_PUBLIC(type) type #endif #endif /* project version */ #define CJSON_VERSION_MAJOR 1 #define CJSON_VERSION_MINOR 7 #define CJSON_VERSION_PATCH 16 #include /* cJSON Types: */ #define cJSON_Invalid (0) #define cJSON_False (1 << 0) #define cJSON_True (1 << 1) #define cJSON_NULL (1 << 2) #define cJSON_Number (1 << 3) #define cJSON_String (1 << 4) #define cJSON_Array (1 << 5) #define cJSON_Object (1 << 6) #define cJSON_Raw (1 << 7) /* raw json */ #define cJSON_IsReference 256 #define cJSON_StringIsConst 512 /* The cJSON structure: */ typedef struct cJSON { /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ struct cJSON *next; struct cJSON *prev; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ struct cJSON *child; /* The type of the item, as above. */ int type; /* The item's string, if type==cJSON_String and type == cJSON_Raw */ char *valuestring; /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ int valueint; /* The item's number, if type==cJSON_Number */ double valuedouble; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ char *string; } cJSON; typedef struct cJSON_Hooks { /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ void *(CJSON_CDECL *malloc_fn)(size_t sz); void (CJSON_CDECL *free_fn)(void *ptr); } cJSON_Hooks; typedef int cJSON_bool; /* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. * This is to prevent stack overflows. */ #ifndef CJSON_NESTING_LIMIT #define CJSON_NESTING_LIMIT 1000 #endif /* returns the version of cJSON as a string */ CJSON_PUBLIC(const char*) cJSON_Version(void); /* Supply malloc, realloc and free functions to cJSON */ CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); /* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ /* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length); /* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ /* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated); /* Render a cJSON entity to text for transfer/storage. */ CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); /* Render a cJSON entity to text for transfer/storage without any formatting. */ CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); /* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); /* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ /* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); /* Delete a cJSON entity and all subentities. */ CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); /* Returns the number of items in an array (or object). */ CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); /* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); /* Get item "string" from object. Case insensitive. */ CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); /* Check item type and return its value */ CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item); /* These functions check the type of an item */ CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); /* These calls create a cJSON item of the appropriate type. */ CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); /* raw json */ CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); /* Create a string where valuestring references a string so * it will not be freed by cJSON_Delete */ CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); /* Create an object/array that only references it's elements so * they will not be freed by cJSON_Delete */ CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); /* These utilities create an Array of count items. * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); /* Append item to the specified array/object. */ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item); CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before * writing to `item->string` */ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); /* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); /* Remove/Detach items from Arrays/Objects. */ CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); /* Update array items. */ CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); /* Duplicate a cJSON item */ CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); /* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will * need to be released. With recurse!=0, it will duplicate any children connected to the item. * The item->next and ->prev pointers are always zero on return from Duplicate. */ /* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); /* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. * The input pointer json cannot point to a read-only address area, such as a string constant, * but should point to a readable and writable address area. */ CJSON_PUBLIC(void) cJSON_Minify(char *json); /* Helper functions for creating and adding items to an object at the same time. * They return the added item or NULL on failure. */ CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); /* When assigning an integer value, it needs to be propagated to valuedouble too. */ #define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) /* helper for the cJSON_SetNumberValue macro */ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); #define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) /* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); /* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/ #define cJSON_SetBoolValue(object, boolValue) ( \ (object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \ (object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \ cJSON_Invalid\ ) /* Macro for iterating over an array or object */ #define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) /* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ CJSON_PUBLIC(void *) cJSON_malloc(size_t size); CJSON_PUBLIC(void) cJSON_free(void *object); #ifdef __cplusplus } #endif #endif estkme-group-lpac-c2fcf5e/cjson/cJSON_ex.c000066400000000000000000000004711504765665400205440ustar00rootroot00000000000000#include "cJSON_ex.h" CJSON_PUBLIC(cJSON *) cJSON_AddStringOrNullToObject(cJSON *const object, const char *const name, const char *const string) { if (string) { return cJSON_AddStringToObject(object, name, string); } else { return cJSON_AddNullToObject(object, name); } } estkme-group-lpac-c2fcf5e/cjson/cJSON_ex.h000066400000000000000000000002361504765665400205500ustar00rootroot00000000000000#pragma once #include "cJSON.h" CJSON_PUBLIC(cJSON *) cJSON_AddStringOrNullToObject(cJSON *const object, const char *const name, const char *const string); estkme-group-lpac-c2fcf5e/cmake/000077500000000000000000000000001504765665400167325ustar00rootroot00000000000000estkme-group-lpac-c2fcf5e/cmake/FindPCSCLite.cmake000066400000000000000000000021431504765665400221030ustar00rootroot00000000000000find_package(PkgConfig REQUIRED) pkg_check_modules(PC_PCSCLITE libpcsclite) find_path(PCSCLITE_INCLUDE_DIR NAMES winscard.h pcsclite.h wintypes.h debuglog.h ifdhandler.h reader.h HINTS ${PC_PCSCLITE_INCLUDEDIR} ${PC_PCSCLITE_INCLUDE_DIRS} ${PC_PCSCLITE_INCLUDE_DIRS}/PCSC ${CMAKE_INSTALL_PREFIX}/include ) find_library(PCSCLITE_LIBRARIES NAMES pcsclite libpcsclite PCSC HINTS ${PC_PCSCLITE_LIBDIR} ${PC_PCSCLITE_LIBRARY_DIRS} ${CMAKE_INSTALL_PREFIX}/lib ${CMAKE_INSTALL_PREFIX}/lib64 ) # handle the QUIETLY and REQUIRED arguments and set PCSCLITE_FOUND to TRUE if # all listed variables are TRUE include(FindPackageHandleStandardArgs) find_package_handle_standard_args(PCSCLite DEFAULT_MSG PCSCLITE_LIBRARIES PCSCLITE_INCLUDE_DIR) mark_as_advanced(PCSCLITE_LIBRARIES PCSCLITE_INCLUDE_DIR) if(PCSCLITE_FOUND AND NOT TARGET PCSCLite::PCSCLite) add_library(PCSCLite::PCSCLite UNKNOWN IMPORTED) set_target_properties(PCSCLite::PCSCLite PROPERTIES IMPORTED_LOCATION "${PCSCLITE_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${PCSCLITE_INCLUDE_DIR}" ) endif() estkme-group-lpac-c2fcf5e/cmake/aarch64-windows-zig.cmake000066400000000000000000000011271504765665400234440ustar00rootroot00000000000000# This requires installing the Zig environment first. # https://ziglang.org/download/ set(CMAKE_SYSTEM_NAME Windows) set(CMAKE_SYSTEM_PROCESSOR "aarch64") set(CMAKE_C_COMPILER "zig" cc -target aarch64-windows-gnu) set(CMAKE_CXX_COMPILER "zig" c++ -target aarch64-windows-gnu) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2 -s") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -s") if (WIN32) set(SCRIPT_SUFFIX ".cmd") else () set(SCRIPT_SUFFIX ".sh") endif () set(CMAKE_AR "${CMAKE_CURRENT_LIST_DIR}/zig-ar${SCRIPT_SUFFIX}") set(CMAKE_RANLIB "${CMAKE_CURRENT_LIST_DIR}/zig-ranlib${SCRIPT_SUFFIX}") estkme-group-lpac-c2fcf5e/cmake/git-version.cmake000066400000000000000000000016251504765665400222060ustar00rootroot00000000000000# from https://github.com/nocnokneo/cmake-git-versioning-example if(GIT_EXECUTABLE) get_filename_component(SRC_DIR ${SRC} DIRECTORY) # Generate a git-describe version string from Git repository tags execute_process( COMMAND ${GIT_EXECUTABLE} describe --always --tags --dirty --match "v*" WORKING_DIRECTORY ${SRC_DIR} OUTPUT_VARIABLE GIT_DESCRIBE_VERSION RESULT_VARIABLE GIT_DESCRIBE_ERROR_CODE OUTPUT_STRIP_TRAILING_WHITESPACE ) if(NOT GIT_DESCRIBE_ERROR_CODE) set(LPAC_VERSION ${GIT_DESCRIBE_VERSION}) endif() endif() # Final fallback: Just use a bogus version string that is semantically older # than anything else and spit out a warning to the developer. if(NOT DEFINED LPAC_VERSION) set(LPAC_VERSION v0.0.0-unknown) message(WARNING "Failed to determine LPAC_VERSION from Git tags. Using default version \"${LPAC_VERSION}\".") endif() configure_file(${SRC} ${DST} @ONLY) estkme-group-lpac-c2fcf5e/cmake/linux-mingw32.cmake000066400000000000000000000007151504765665400223620ustar00rootroot00000000000000set(CMAKE_SYSTEM_NAME Windows) set(TOOLCHAIN_PREFIX i686-w64-mingw32) set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc) set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++) set(CMAKE_Fortran_COMPILER ${TOOLCHAIN_PREFIX}-gfortran) set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres) set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX}) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) estkme-group-lpac-c2fcf5e/cmake/linux-mingw64-woa.cmake000066400000000000000000000010361504765665400231500ustar00rootroot00000000000000# Please download the toolchain from https://github.com/Windows-on-ARM-Experiments/mingw-woarm64-build # and add it to your PATH set(CMAKE_SYSTEM_NAME Windows) set(TOOLCHAIN_PREFIX aarch64-w64-mingw32) set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc) set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++) set(CMAKE_Fortran_COMPILER ${TOOLCHAIN_PREFIX}-gfortran) set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) estkme-group-lpac-c2fcf5e/cmake/linux-mingw64.cmake000066400000000000000000000007171504765665400223710ustar00rootroot00000000000000set(CMAKE_SYSTEM_NAME Windows) set(TOOLCHAIN_PREFIX x86_64-w64-mingw32) set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc) set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++) set(CMAKE_Fortran_COMPILER ${TOOLCHAIN_PREFIX}-gfortran) set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres) set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX}) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) estkme-group-lpac-c2fcf5e/cmake/zig-ar.cmd000077500000000000000000000000231504765665400206060ustar00rootroot00000000000000@echo off zig ar %*estkme-group-lpac-c2fcf5e/cmake/zig-ar.sh000077500000000000000000000000261504765665400204600ustar00rootroot00000000000000#!/bin/sh zig ar "$@" estkme-group-lpac-c2fcf5e/cmake/zig-ranlib.cmd000077500000000000000000000000271504765665400214570ustar00rootroot00000000000000@echo off zig ranlib %*estkme-group-lpac-c2fcf5e/cmake/zig-ranlib.sh000077500000000000000000000000321504765665400213220ustar00rootroot00000000000000#!/bin/sh zig ranlib "$@" estkme-group-lpac-c2fcf5e/dlfcn-win32/000077500000000000000000000000001504765665400177005ustar00rootroot00000000000000estkme-group-lpac-c2fcf5e/dlfcn-win32/CMakeLists.txt000066400000000000000000000003241504765665400224370ustar00rootroot00000000000000aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} LIB_DLFCN_SRCS) add_library(dlfcn-win32 STATIC ${LIB_DLFCN_SRCS}) target_include_directories(dlfcn-win32 PUBLIC $) estkme-group-lpac-c2fcf5e/dlfcn-win32/LICENSE000066400000000000000000000017761504765665400207200ustar00rootroot00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.estkme-group-lpac-c2fcf5e/dlfcn-win32/dlfcn.c000066400000000000000000000701331504765665400211360ustar00rootroot00000000000000/* * dlfcn-win32 * Copyright (c) 2007 Ramiro Polla * Copyright (c) 2015 Tiancheng "Timothy" Gu * Copyright (c) 2019 Pali Rohár * Copyright (c) 2020 Ralf Habacker * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #ifdef _DEBUG #define _CRTDBG_MAP_ALLOC #include #include #endif #include #include #include /* Older versions do not have this type */ #if _WIN32_WINNT < 0x0500 typedef ULONG ULONG_PTR; #endif /* Older SDK versions do not have these macros */ #ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS #define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 0x4 #endif #ifndef GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT #define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 0x2 #endif #ifndef IMAGE_NT_OPTIONAL_HDR_MAGIC #ifdef _WIN64 #define IMAGE_NT_OPTIONAL_HDR_MAGIC 0x20b #else #define IMAGE_NT_OPTIONAL_HDR_MAGIC 0x10b #endif #endif #ifndef IMAGE_DIRECTORY_ENTRY_IAT #define IMAGE_DIRECTORY_ENTRY_IAT 12 #endif #ifndef LOAD_WITH_ALTERED_SEARCH_PATH #define LOAD_WITH_ALTERED_SEARCH_PATH 0x8 #endif #ifdef _MSC_VER #if _MSC_VER >= 1000 /* https://docs.microsoft.com/en-us/cpp/intrinsics/returnaddress */ #pragma intrinsic( _ReturnAddress ) #else /* On older version read return address from the value on stack pointer + 4 of * the caller. Caller stack pointer is stored in EBP register but only when * the EBP register is not optimized out. Usage of _alloca() function prevent * EBP register optimization. Read value of EBP + 4 via inline assembly. And * because inline assembly does not have a return value, put it into naked * function which does not have prologue and epilogue and preserve registers. */ __declspec( naked ) static void *_ReturnAddress( void ) { __asm mov eax, [ebp+4] __asm ret } #define _ReturnAddress( ) ( _alloca(1), _ReturnAddress( ) ) #endif #else /* https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html */ #ifndef _ReturnAddress #define _ReturnAddress( ) ( __builtin_extract_return_addr( __builtin_return_address( 0 ) ) ) #endif #endif #ifdef DLFCN_WIN32_SHARED #define DLFCN_WIN32_EXPORTS #endif #include "dlfcn.h" #if defined( _MSC_VER ) && _MSC_VER >= 1300 /* https://docs.microsoft.com/en-us/cpp/cpp/noinline */ #define DLFCN_NOINLINE __declspec( noinline ) #elif defined( __GNUC__ ) && ( ( __GNUC__ > 3 ) || ( __GNUC__ == 3 && __GNUC_MINOR__ >= 1 ) ) /* https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html */ #define DLFCN_NOINLINE __attribute__(( noinline )) #else #define DLFCN_NOINLINE #endif /* Note: * MSDN says these functions are not thread-safe. We make no efforts to have * any kind of thread safety. */ typedef struct local_object { HMODULE hModule; struct local_object *previous; struct local_object *next; } local_object; static local_object first_object; /* These functions implement a double linked list for the local objects. */ static local_object *local_search( HMODULE hModule ) { local_object *pobject; if( hModule == NULL ) return NULL; for( pobject = &first_object; pobject; pobject = pobject->next ) if( pobject->hModule == hModule ) return pobject; return NULL; } static BOOL local_add( HMODULE hModule ) { local_object *pobject; local_object *nobject; if( hModule == NULL ) return TRUE; pobject = local_search( hModule ); /* Do not add object again if it's already on the list */ if( pobject != NULL ) return TRUE; for( pobject = &first_object; pobject->next; pobject = pobject->next ); nobject = (local_object *) malloc( sizeof( local_object ) ); if( !nobject ) return FALSE; pobject->next = nobject; nobject->next = NULL; nobject->previous = pobject; nobject->hModule = hModule; return TRUE; } static void local_rem( HMODULE hModule ) { local_object *pobject; if( hModule == NULL ) return; pobject = local_search( hModule ); if( pobject == NULL ) return; if( pobject->next ) pobject->next->previous = pobject->previous; if( pobject->previous ) pobject->previous->next = pobject->next; free( pobject ); } /* POSIX says dlerror( ) doesn't have to be thread-safe, so we use one * static buffer. * MSDN says the buffer cannot be larger than 64K bytes, so we set it to * the limit. */ static char error_buffer[65535]; static BOOL error_occurred; static void save_err_str( const char *str, DWORD dwMessageId ) { DWORD ret; size_t pos, len; len = strlen( str ); if( len > sizeof( error_buffer ) - 5 ) len = sizeof( error_buffer ) - 5; /* Format error message to: * "": */ pos = 0; error_buffer[pos++] = '"'; memcpy( error_buffer + pos, str, len ); pos += len; error_buffer[pos++] = '"'; error_buffer[pos++] = ':'; error_buffer[pos++] = ' '; ret = FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwMessageId, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), error_buffer + pos, (DWORD) ( sizeof( error_buffer ) - pos ), NULL ); pos += ret; /* When FormatMessageA() fails it returns zero and does not touch buffer * so add trailing null byte */ if( ret == 0 ) error_buffer[pos] = '\0'; if( pos > 1 ) { /* POSIX says the string must not have trailing */ if( error_buffer[pos-2] == '\r' && error_buffer[pos-1] == '\n' ) error_buffer[pos-2] = '\0'; } error_occurred = TRUE; } static void save_err_ptr_str( const void *ptr, DWORD dwMessageId ) { char ptr_buf[2 + 2 * sizeof( ptr ) + 1]; char num; size_t i; ptr_buf[0] = '0'; ptr_buf[1] = 'x'; for( i = 0; i < 2 * sizeof( ptr ); i++ ) { num = (char) ( ( ( (ULONG_PTR) ptr ) >> ( 8 * sizeof( ptr ) - 4 * ( i + 1 ) ) ) & 0xF ); ptr_buf[2 + i] = num + ( ( num < 0xA ) ? '0' : ( 'A' - 0xA ) ); } ptr_buf[2 + 2 * sizeof( ptr )] = 0; save_err_str( ptr_buf, dwMessageId ); } static UINT MySetErrorMode( UINT uMode ) { static BOOL (WINAPI *SetThreadErrorModePtr)(DWORD, DWORD *) = NULL; static BOOL failed = FALSE; HMODULE kernel32; DWORD oldMode; if( !failed && SetThreadErrorModePtr == NULL ) { kernel32 = GetModuleHandleA( "Kernel32.dll" ); if( kernel32 != NULL ) SetThreadErrorModePtr = (BOOL (WINAPI *)(DWORD, DWORD *)) (LPVOID) GetProcAddress( kernel32, "SetThreadErrorMode" ); if( SetThreadErrorModePtr == NULL ) failed = TRUE; } if( !failed ) { if( !SetThreadErrorModePtr( uMode, &oldMode ) ) return 0; else return oldMode; } else { return SetErrorMode( uMode ); } } static HMODULE MyGetModuleHandleFromAddress( const void *addr ) { static BOOL (WINAPI *GetModuleHandleExAPtr)(DWORD, LPCSTR, HMODULE *) = NULL; static BOOL failed = FALSE; HMODULE kernel32; HMODULE hModule; MEMORY_BASIC_INFORMATION info; size_t sLen; if( !failed && GetModuleHandleExAPtr == NULL ) { kernel32 = GetModuleHandleA( "Kernel32.dll" ); if( kernel32 != NULL ) GetModuleHandleExAPtr = (BOOL (WINAPI *)(DWORD, LPCSTR, HMODULE *)) (LPVOID) GetProcAddress( kernel32, "GetModuleHandleExA" ); if( GetModuleHandleExAPtr == NULL ) failed = TRUE; } if( !failed ) { /* If GetModuleHandleExA is available use it with GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS */ if( !GetModuleHandleExAPtr( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, addr, &hModule ) ) return NULL; } else { /* To get HMODULE from address use undocumented hack from https://stackoverflow.com/a/2396380 * The HMODULE of a DLL is the same value as the module's base address. */ sLen = VirtualQuery( addr, &info, sizeof( info ) ); if( sLen != sizeof( info ) ) return NULL; hModule = (HMODULE) info.AllocationBase; } return hModule; } /* Load Psapi.dll at runtime, this avoids linking caveat */ static BOOL MyEnumProcessModules( HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded ) { static BOOL (WINAPI *EnumProcessModulesPtr)(HANDLE, HMODULE *, DWORD, LPDWORD) = NULL; static BOOL failed = FALSE; UINT uMode; HMODULE psapi; if( failed ) return FALSE; if( EnumProcessModulesPtr == NULL ) { /* Windows 7 and newer versions have K32EnumProcessModules in Kernel32.dll which is always pre-loaded */ psapi = GetModuleHandleA( "Kernel32.dll" ); if( psapi != NULL ) EnumProcessModulesPtr = (BOOL (WINAPI *)(HANDLE, HMODULE *, DWORD, LPDWORD)) (LPVOID) GetProcAddress( psapi, "K32EnumProcessModules" ); /* Windows Vista and older version have EnumProcessModules in Psapi.dll which needs to be loaded */ if( EnumProcessModulesPtr == NULL ) { /* Do not let Windows display the critical-error-handler message box */ uMode = MySetErrorMode( SEM_FAILCRITICALERRORS ); psapi = LoadLibraryA( "Psapi.dll" ); if( psapi != NULL ) { EnumProcessModulesPtr = (BOOL (WINAPI *)(HANDLE, HMODULE *, DWORD, LPDWORD)) (LPVOID) GetProcAddress( psapi, "EnumProcessModules" ); if( EnumProcessModulesPtr == NULL ) FreeLibrary( psapi ); } MySetErrorMode( uMode ); } if( EnumProcessModulesPtr == NULL ) { failed = TRUE; return FALSE; } } return EnumProcessModulesPtr( hProcess, lphModule, cb, lpcbNeeded ); } DLFCN_EXPORT void *dlopen( const char *file, int mode ) { HMODULE hModule; UINT uMode; error_occurred = FALSE; /* Do not let Windows display the critical-error-handler message box */ uMode = MySetErrorMode( SEM_FAILCRITICALERRORS ); if( file == NULL ) { /* POSIX says that if the value of file is NULL, a handle on a global * symbol object must be provided. That object must be able to access * all symbols from the original program file, and any objects loaded * with the RTLD_GLOBAL flag. * The return value from GetModuleHandle( ) allows us to retrieve * symbols only from the original program file. EnumProcessModules() is * used to access symbols from other libraries. For objects loaded * with the RTLD_LOCAL flag, we create our own list later on. They are * excluded from EnumProcessModules() iteration. */ hModule = GetModuleHandle( NULL ); if( !hModule ) save_err_str( "(null)", GetLastError( ) ); } else { HANDLE hCurrentProc; DWORD dwProcModsBefore, dwProcModsAfter; char lpFileName[MAX_PATH]; size_t i, len; len = strlen( file ); if( len >= sizeof( lpFileName ) ) { save_err_str( file, ERROR_FILENAME_EXCED_RANGE ); hModule = NULL; } else { /* MSDN says backslashes *must* be used instead of forward slashes. */ for( i = 0; i < len; i++ ) { if( file[i] == '/' ) lpFileName[i] = '\\'; else lpFileName[i] = file[i]; } lpFileName[len] = '\0'; hCurrentProc = GetCurrentProcess( ); if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwProcModsBefore ) == 0 ) dwProcModsBefore = 0; /* POSIX says the search path is implementation-defined. * LOAD_WITH_ALTERED_SEARCH_PATH is used to make it behave more closely * to UNIX's search paths (start with system folders instead of current * folder). * FIXME: Remove LOAD_WITH_ALTERED_SEARCH_PATH because it lead to Undefined * Behavior and doesn't provide expected search paths. * See also: https://github.com/dlfcn-win32/dlfcn-win32/issues/104 */ hModule = LoadLibraryExA( lpFileName, NULL, 0 ); if( !hModule ) { save_err_str( lpFileName, GetLastError( ) ); } else { if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwProcModsAfter ) == 0 ) dwProcModsAfter = 0; /* If the object was loaded with RTLD_LOCAL, add it to list of local * objects, so that its symbols cannot be retrieved even if the handle for * the original program file is passed. POSIX says that if the same * file is specified in multiple invocations, and any of them are * RTLD_GLOBAL, even if any further invocations use RTLD_LOCAL, the * symbols will remain global. If number of loaded modules was not * changed after calling LoadLibraryEx(), it means that library was * already loaded. */ if( (mode & RTLD_LOCAL) && dwProcModsBefore != dwProcModsAfter ) { if( !local_add( hModule ) ) { save_err_str( lpFileName, ERROR_NOT_ENOUGH_MEMORY ); FreeLibrary( hModule ); hModule = NULL; } } else if( !(mode & RTLD_LOCAL) && dwProcModsBefore == dwProcModsAfter ) { local_rem( hModule ); } } } } /* Return to previous state of the error-mode bit flags. */ MySetErrorMode( uMode ); return (void *) hModule; } DLFCN_EXPORT int dlclose( void *handle ) { HMODULE hModule = (HMODULE) handle; BOOL ret; error_occurred = FALSE; ret = FreeLibrary( hModule ); /* If the object was loaded with RTLD_LOCAL, remove it from list of local * objects. */ if( ret ) local_rem( hModule ); else save_err_ptr_str( handle, GetLastError( ) ); /* dlclose's return value in inverted in relation to FreeLibrary's. */ ret = !ret; return (int) ret; } DLFCN_NOINLINE /* Needed for _ReturnAddress() */ DLFCN_EXPORT void *dlsym( void *handle, const char *name ) { FARPROC symbol; HMODULE hCaller; HMODULE hModule; DWORD dwMessageId; error_occurred = FALSE; symbol = NULL; hCaller = NULL; hModule = GetModuleHandle( NULL ); dwMessageId = 0; if( handle == RTLD_DEFAULT ) { /* The symbol lookup happens in the normal global scope; that is, * a search for a symbol using this handle would find the same * definition as a direct use of this symbol in the program code. * So use same lookup procedure as when filename is NULL. */ handle = hModule; } else if( handle == RTLD_NEXT ) { /* Specifies the next object after this one that defines name. * This one refers to the object containing the invocation of dlsym(). * The next object is the one found upon the application of a load * order symbol resolution algorithm. To get caller function of dlsym() * use _ReturnAddress() intrinsic. To get HMODULE of caller function * use MyGetModuleHandleFromAddress() which calls either standard * GetModuleHandleExA() function or hack via VirtualQuery(). */ hCaller = MyGetModuleHandleFromAddress( _ReturnAddress( ) ); if( hCaller == NULL ) { dwMessageId = ERROR_INVALID_PARAMETER; goto end; } } if( handle != RTLD_NEXT ) { symbol = GetProcAddress( (HMODULE) handle, name ); if( symbol != NULL ) goto end; } /* If the handle for the original program file is passed, also search * in all globally loaded objects. */ if( hModule == handle || handle == RTLD_NEXT ) { HANDLE hCurrentProc; HMODULE *modules; DWORD cbNeeded; DWORD dwSize; size_t i; hCurrentProc = GetCurrentProcess( ); /* GetModuleHandle( NULL ) only returns the current program file. So * if we want to get ALL loaded module including those in linked DLLs, * we have to use EnumProcessModules( ). */ if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwSize ) != 0 ) { modules = malloc( dwSize ); if( modules ) { if( MyEnumProcessModules( hCurrentProc, modules, dwSize, &cbNeeded ) != 0 && dwSize == cbNeeded ) { for( i = 0; i < dwSize / sizeof( HMODULE ); i++ ) { if( handle == RTLD_NEXT && hCaller ) { /* Next modules can be used for RTLD_NEXT */ if( hCaller == modules[i] ) hCaller = NULL; continue; } if( local_search( modules[i] ) ) continue; symbol = GetProcAddress( modules[i], name ); if( symbol != NULL ) { free( modules ); goto end; } } } free( modules ); } else { dwMessageId = ERROR_NOT_ENOUGH_MEMORY; goto end; } } } end: if( symbol == NULL ) { if( !dwMessageId ) dwMessageId = ERROR_PROC_NOT_FOUND; save_err_str( name, dwMessageId ); } return *(void **) (&symbol); } DLFCN_EXPORT char *dlerror( void ) { /* If this is the second consecutive call to dlerror, return NULL */ if( !error_occurred ) return NULL; /* POSIX says that invoking dlerror( ) a second time, immediately following * a prior invocation, shall result in NULL being returned. */ error_occurred = FALSE; return error_buffer; } /* See https://docs.microsoft.com/en-us/archive/msdn-magazine/2002/march/inside-windows-an-in-depth-look-into-the-win32-portable-executable-file-format-part-2 * for details */ /* Get specific image section */ static BOOL get_image_section( HMODULE module, int index, void **ptr, DWORD *size ) { IMAGE_DOS_HEADER *dosHeader; IMAGE_NT_HEADERS *ntHeaders; IMAGE_OPTIONAL_HEADER *optionalHeader; dosHeader = (IMAGE_DOS_HEADER *) module; if( dosHeader->e_magic != IMAGE_DOS_SIGNATURE ) return FALSE; ntHeaders = (IMAGE_NT_HEADERS *) ( (BYTE *) dosHeader + dosHeader->e_lfanew ); if( ntHeaders->Signature != IMAGE_NT_SIGNATURE ) return FALSE; optionalHeader = &ntHeaders->OptionalHeader; if( optionalHeader->Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC ) return FALSE; if( index < 0 || index >= IMAGE_NUMBEROF_DIRECTORY_ENTRIES || index >= optionalHeader->NumberOfRvaAndSizes ) return FALSE; if( optionalHeader->DataDirectory[index].Size == 0 || optionalHeader->DataDirectory[index].VirtualAddress == 0 ) return FALSE; if( size != NULL ) *size = optionalHeader->DataDirectory[index].Size; *ptr = (void *)( (BYTE *) module + optionalHeader->DataDirectory[index].VirtualAddress ); return TRUE; } /* Return symbol name for a given address from export table */ static const char *get_export_symbol_name( HMODULE module, IMAGE_EXPORT_DIRECTORY *ied, const void *addr, void **func_address ) { DWORD i; void *candidateAddr = NULL; int candidateIndex = -1; BYTE *base = (BYTE *) module; DWORD *functionAddressesOffsets = (DWORD *) (base + (DWORD) ied->AddressOfFunctions); DWORD *functionNamesOffsets = (DWORD *) (base + (DWORD) ied->AddressOfNames); USHORT *functionNameOrdinalsIndexes = (USHORT *) (base + (DWORD) ied->AddressOfNameOrdinals); for( i = 0; i < ied->NumberOfFunctions; i++ ) { if( (void *) ( base + functionAddressesOffsets[i] ) > addr || candidateAddr >= (void *) ( base + functionAddressesOffsets[i] ) ) continue; candidateAddr = (void *) ( base + functionAddressesOffsets[i] ); candidateIndex = i; } if( candidateIndex == -1 ) return NULL; *func_address = candidateAddr; for( i = 0; i < ied->NumberOfNames; i++ ) { if( functionNameOrdinalsIndexes[i] == candidateIndex ) return (const char *) ( base + functionNamesOffsets[i] ); } return NULL; } static BOOL is_valid_address( const void *addr ) { MEMORY_BASIC_INFORMATION info; size_t result; if( addr == NULL ) return FALSE; /* check valid pointer */ result = VirtualQuery( addr, &info, sizeof( info ) ); if( result == 0 || info.AllocationBase == NULL || info.AllocationProtect == 0 || info.AllocationProtect == PAGE_NOACCESS ) return FALSE; return TRUE; } #if defined(_M_ARM64) || defined(__aarch64__) static INT64 sign_extend(UINT64 value, UINT bits) { const UINT left = 64 - bits; const INT64 m1 = -1; const INT64 wide = (INT64) (value << left); const INT64 sign = ( wide < 0 ) ? ( m1 << left ) : 0; return value | sign; } #endif /* Return state if address points to an import thunk * * On x86, an import thunk is setup with a 'jmp' instruction followed by an * absolute address (32bit) or relative offset (64bit) pointing into * the import address table (iat), which is partially maintained by * the runtime linker. * * On ARM64, an import thunk is also a relative jump pointing into the * import address table, implemented by the following three instructions: * * adrp x16, [page_offset] * Calculates the page address (aligned to 4KB) the IAT is at, based * on the value of x16, with page_offset. * * ldr x16, [x16, offset] * Calculates the final IAT address, x16 <- x16 + offset. * * br x16 * Jump to the address in x16. * * The register used here is hardcoded to be x16. */ static BOOL is_import_thunk( const void *addr ) { #if defined(_M_ARM64) || defined(__aarch64__) ULONG opCode1 = * (ULONG *) ( (BYTE *) addr ); ULONG opCode2 = * (ULONG *) ( (BYTE *) addr + 4 ); ULONG opCode3 = * (ULONG *) ( (BYTE *) addr + 8 ); return (opCode1 & 0x9f00001f) == 0x90000010 /* adrp x16, [page_offset] */ && (opCode2 & 0xffe003ff) == 0xf9400210 /* ldr x16, [x16, offset] */ && opCode3 == 0xd61f0200 /* br x16 */ ? TRUE : FALSE; #else return *(short *) addr == 0x25ff ? TRUE : FALSE; #endif } /* Return address from the import address table (iat), * if the original address points to a thunk table entry. */ static void *get_address_from_import_address_table( void *iat, DWORD iat_size, const void *addr ) { BYTE *thkp = (BYTE *) addr; #if defined(_M_ARM64) || defined(__aarch64__) /* * typical import thunk in ARM64: * 0x7ff772ae78c0 <+25760>: adrp x16, 1 * 0x7ff772ae78c4 <+25764>: ldr x16, [x16, #0xdc0] * 0x7ff772ae78c8 <+25768>: br x16 */ ULONG opCode1 = * (ULONG *) ( (BYTE *) addr ); ULONG opCode2 = * (ULONG *) ( (BYTE *) addr + 4 ); /* Extract the offset from adrp instruction */ UINT64 pageLow2 = (opCode1 >> 29) & 3; UINT64 pageHigh19 = (opCode1 >> 5) & ~(~0ull << 19); INT64 page = sign_extend((pageHigh19 << 2) | pageLow2, 21) << 12; /* Extract the offset from ldr instruction */ UINT64 offset = ((opCode2 >> 10) & ~(~0ull << 12)) << 3; /* Calculate the final address */ BYTE *ptr = (BYTE *) ( (ULONG64) thkp & ~0xfffull ) + page + offset; #else /* Get offset from thunk table (after instruction 0xff 0x25) * 4018c8 <_VirtualQuery>: ff 25 4a 8a 00 00 */ ULONG offset = *(ULONG *)( thkp + 2 ); #if defined(_M_AMD64) || defined(__x86_64__) /* On 64 bit the offset is relative * 4018c8: ff 25 4a 8a 00 00 jmpq *0x8a4a(%rip) # 40a318 <__imp_VirtualQuery> * And can be also negative (MSVC in WDK) * 100002f20: ff 25 3a e1 ff ff jmpq *-0x1ec6(%rip) # 0x100001060 * So cast to signed LONG type */ BYTE *ptr = (BYTE *)( thkp + 6 + (LONG) offset ); #else /* On 32 bit the offset is absolute * 4019b4: ff 25 90 71 40 00 jmp *0x40719 */ BYTE *ptr = (BYTE *) offset; #endif #endif if( !is_valid_address( ptr ) || ptr < (BYTE *) iat || ptr > (BYTE *) iat + iat_size ) return NULL; return *(void **) ptr; } /* Holds module filename */ static char module_filename[2*MAX_PATH]; static BOOL fill_info( const void *addr, Dl_info *info ) { HMODULE hModule; DWORD dwSize; IMAGE_EXPORT_DIRECTORY *ied; void *funcAddress = NULL; /* Get module of the specified address */ hModule = MyGetModuleHandleFromAddress( addr ); if( hModule == NULL ) return FALSE; dwSize = GetModuleFileNameA( hModule, module_filename, sizeof( module_filename ) ); if( dwSize == 0 || dwSize == sizeof( module_filename ) ) return FALSE; info->dli_fname = module_filename; info->dli_fbase = (void *) hModule; /* Find function name and function address in module's export table */ if( get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_EXPORT, (void **) &ied, NULL ) ) info->dli_sname = get_export_symbol_name( hModule, ied, addr, &funcAddress ); else info->dli_sname = NULL; info->dli_saddr = info->dli_sname == NULL ? NULL : funcAddress != NULL ? funcAddress : (void *) addr; return TRUE; } DLFCN_EXPORT int dladdr( const void *addr, Dl_info *info ) { if( info == NULL ) return 0; if( !is_valid_address( addr ) ) return 0; if( is_import_thunk( addr ) ) { void *iat; DWORD iatSize; HMODULE hModule; /* Get module of the import thunk address */ hModule = MyGetModuleHandleFromAddress( addr ); if( hModule == NULL ) return 0; if( !get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_IAT, &iat, &iatSize ) ) { /* Fallback for cases where the iat is not defined, * for example i586-mingw32msvc-gcc */ IMAGE_IMPORT_DESCRIPTOR *iid; DWORD iidSize; if( !get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_IMPORT, (void **) &iid, &iidSize ) ) return 0; if( iid == NULL || iid->Characteristics == 0 || iid->FirstThunk == 0 ) return 0; iat = (void *)( (BYTE *) hModule + (DWORD) iid->FirstThunk ); /* We assume that in this case iid and iat's are in linear order */ iatSize = iidSize - (DWORD) ( (BYTE *) iat - (BYTE *) iid ); } addr = get_address_from_import_address_table( iat, iatSize, addr ); if( !is_valid_address( addr ) ) return 0; } if( !fill_info( addr, info ) ) return 0; return 1; } #ifdef DLFCN_WIN32_SHARED BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ) { (void) hinstDLL; (void) fdwReason; (void) lpvReserved; return TRUE; } #endif estkme-group-lpac-c2fcf5e/dlfcn-win32/dlfcn.h000066400000000000000000000062341504765665400211440ustar00rootroot00000000000000/* * dlfcn-win32 * Copyright (c) 2007 Ramiro Polla * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #ifndef DLFCN_H #define DLFCN_H #ifdef __cplusplus extern "C" { #endif #if defined(DLFCN_WIN32_SHARED) #if defined(DLFCN_WIN32_EXPORTS) # define DLFCN_EXPORT __declspec(dllexport) #else # define DLFCN_EXPORT __declspec(dllimport) #endif #else # define DLFCN_EXPORT #endif /* Relocations are performed when the object is loaded. */ #define RTLD_NOW 0 /* Relocations are performed at an implementation-defined time. * Windows API does not support lazy symbol resolving (when first reference * to a given symbol occurs). So RTLD_LAZY implementation is same as RTLD_NOW. */ #define RTLD_LAZY RTLD_NOW /* All symbols are available for relocation processing of other modules. */ #define RTLD_GLOBAL (1 << 1) /* All symbols are not made available for relocation processing by other modules. */ #define RTLD_LOCAL (1 << 2) /* These two were added in The Open Group Base Specifications Issue 6. * Note: All other RTLD_* flags in any dlfcn.h are not standard compliant. */ /* The symbol lookup happens in the normal global scope. */ #define RTLD_DEFAULT ((void *)0) /* Specifies the next object after this one that defines name. */ #define RTLD_NEXT ((void *)-1) /* Structure filled in by dladdr() */ typedef struct dl_info { const char *dli_fname; /* Filename of defining object (thread unsafe and reused on every call to dladdr) */ void *dli_fbase; /* Load address of that object */ const char *dli_sname; /* Name of nearest lower symbol */ void *dli_saddr; /* Exact value of nearest symbol */ } Dl_info; /* Open a symbol table handle. */ DLFCN_EXPORT void *dlopen(const char *file, int mode); /* Close a symbol table handle. */ DLFCN_EXPORT int dlclose(void *handle); /* Get the address of a symbol from a symbol table handle. */ DLFCN_EXPORT void *dlsym(void *handle, const char *name); /* Get diagnostic information. */ DLFCN_EXPORT char *dlerror(void); /* Translate address to symbolic information (no POSIX standard) */ DLFCN_EXPORT int dladdr(const void *addr, Dl_info *info); #ifdef __cplusplus } #endif #endif /* DLFCN_H */ estkme-group-lpac-c2fcf5e/docs/000077500000000000000000000000001504765665400166025ustar00rootroot00000000000000estkme-group-lpac-c2fcf5e/docs/DEVELOPERS.md000066400000000000000000000053241504765665400206000ustar00rootroot00000000000000# Developer Manual ## Coding Standard lpac is written with C99 and compatible with [SGP.22 version 2.2.2](https://www.gsma.com/solutions-and-impact/technologies/esim/wp-content/uploads/2020/06/SGP.22-v2.2.2.pdf). ## How to Compile ### CMake lpac uses CMake as its build system. Common build steps for all OSs look as follows: ``` bash # clone this repo in the top-level folder git clone https://github.com/estkme-group/lpac.git cd lpac # configuration (this step can also include options -DOPTION=VALUE) cmake -B build # compilation cmake --build build # installation (optionally) cmake --install build ``` The resulting binary can then be found under `build/output` folder. ### Linux #### Debian/Ubuntu Require `build-essential` `libpcsclite-dev` `libcurl4-openssl-dev` installed. If you want to get a Deb package, run `cmake -B build -DCPACK_GENERATOR=DEB` then `cmake --build build`. #### Droidian Same as normal Debian/Ubuntu, however, in order to build the GBinder backends, you will need `libgbinder-dev`, `glib2.0-dev`, and you will have to pass `-DLPAC_WITH_APDU_GBINDER=ON` when invoking `cmake`. --- ### macOS Install [Homebrew](https://brew.sh/). Execute the same commands as you would do in Debian. --- ### Windows(x86_64) Windows needs libcurl.dll to run. Download libcurl from and place it as `libcurl.dll` aside `lpac.exe`. Install prerequisites and run CMake commands. #### Build on Linux(MINGW) Require `build-essential` `cmake` `git` `g++` `libpcsclite-dev` `libcurl4-openssl-dev` `gcc-mingw-w64` `g++-mingw-w64` installed. #### Build on Windows(MSYS2) Require `mingw-w64-x86_64-cmake` `mingw-w64-x86_64-gcc` installed. #### Build on Windows(Cygwin) Require `gcc-core` `gcc-g++` `make` `cmake` `unzip` `wget` installed. To run it outside Cygwin shell, you need copy `cygwin1.dll` to the program folder to distribute. `cygwin1.dll` is located in `C:\cygwin64\bin\cygwin1.dll` (Default Cygwin installation location) --- ### Windows on ARM #### Cross compile on Windows/Linux host (arm64, x86_64 and more architecture) with zig See [aarch64-windows-zig.cmake](../cmake/aarch64-windows-zig.cmake) #### Cross compile on Linux x86_64 host (GNU toolchain) See [linux-mingw64-woa.cmake](../cmake/linux-mingw64-woa.cmake) #### Build on Native Windows on ARM (MSYS2) It is possible to build on **WoA devices** with [MSYS2 ARM64 Support](https://www.msys2.org/wiki/arm64/) You may need to install `mingw-w64-clang-aarch64-cmake`, `mingw-w64-clang-aarch64-clang` and modify `cmake/linux-mingw64.cmake`(replace toolchain). Download prebuilt curl dll is also needed. Refer to the previous compilation steps. ## Debug Please see [debug environment variables](ENVVARS.md#debug) estkme-group-lpac-c2fcf5e/docs/ENVVARS.md000066400000000000000000000043121504765665400202500ustar00rootroot00000000000000# Environment Variables ## General * `LPAC_CUSTOM_ES10X_MSS`: specify maximum segment size for ES10x APDU backend. (default: 120, min: 6, max: 255) * `LPAC_CUSTOM_ISD_R_AID`: specify which AID will be used to open the logic channel. (hex string, 32 chars) * `LPAC_APDU`: specify which APDU backend will be used. Values: - `at`: Use the AT command interface via a serial device on different platforms. - On Unix-like platforms (Linux, BSD), use serial devices such as `/dev/ttyUSB0`. - On Windows platforms, use serial COM ports such as `COM3`. - `pcsc`: use PC/SC Smart Card API - `stdio`: use standard input/output - `qmi`: use QMI - `qmi_qrtr`: use QMI over QRTR - `mbim`: use MBIM - GBinder-based backends for `libhybris` (Halium) distributions: - `gbinder_hidl`: use HIDL IRadio (SoC launched before Android 13) * `LPAC_HTTP`: specify which HTTP backend will be used. - `curl`: use libcurl - `stdio`: use standard input/output * `LPAC_APDU_AT_DEVICE`: specify which serial port device will be used by AT APDU backend. * `LPAC_APDU_PCSC_DRV_IFID`: specify which PC/SC interface index will be used by PC/SC APDU backend. * `LPAC_APDU_PCSC_DRV_NAME`: specify which PC/SC interface name will be used by PC/SC APDU backend. * `LPAC_APDU_PCSC_DRV_IGNORE_NAME`: specify which PC/SC interface names will be ignored by PC/SC APDU backend. (use semicolon (`;`) split, for example: `Yubico;Canokeys`). * `LPAC_APDU_QMI_UIM_SLOT`: specify which UIM slot will be used by QMI APDU backend. (default: 1, slot number starts from 1) * `LPAC_APDU_QMI_DEVICE`: specify which QMI device will be used by QMI APDU backend. * `LPAC_APDU_MBIM_UIM_SLOT`: specify which UIM slot will be used by MBIM APDU backend. (default: 1, slot number starts from 1) * `LPAC_APDU_MBIM_USE_PROXY`: tell the MBIM APDU backend to use the mbim-proxy. (boolean) * `LPAC_APDU_MBIM_DEVICE`: specify which MBIM device will be used by MBIM APDU backend. (default: `/dev/cdc-wdm0`) ## Debug * `LIBEUICC_DEBUG_APDU`: enable debug output for APDU. * `LIBEUICC_DEBUG_HTTP`: enable debug output for HTTP. * `LPAC_APDU_AT_DEBUG`: enable debug output for AT APDU backend. (boolean) * `LPAC_APDU_GBINDER_DEBUG`: enable debug output for GBinder APDU backend. (boolean) estkme-group-lpac-c2fcf5e/docs/FAQ.md000066400000000000000000000025141504765665400175350ustar00rootroot00000000000000## FAQ ### Any subcommand of lpac will get error message like "SCardListReaders() failed:" and 8-digits error code, such as 80100069, 80100066, 8010002E, 8010000F and so on. - [80100069] means your UICC is not plugged correctly - [80100066] means your card has no response, please clean the pin and plug in again - [8010002E] means communication error - [8010000F] means the card is not a eUICC, or detect wrong card reader like Yubikey. For latter one, you can use `lpac driver apdu list` to list all reader and use `$DRIVER_IFID` to specify correct card reader - for others, Google is your friend. [80100069]: https://pcsclite.apdu.fr/api/group__ErrorCodes.html#gaa2efd953946973972b1afc5d0343820c [80100066]: https://pcsclite.apdu.fr/api/group__ErrorCodes.html#ga359a9e85e3b7c83c76507a096452b74f [8010002E]: https://pcsclite.apdu.fr/api/group__ErrorCodes.html#ga81b59e9319d3fcd0d957d98781b3ebd2 [8010000F]: https://pcsclite.apdu.fr/api/group__ErrorCodes.html#ga36d821a0458f935ddbe345f10408a988 ### I can't download eSIM profile of xxx. The verification of SM-DP+ servers of telecom operators is diverse. Please check whether the parameters you enter are consistent with those provided to you by the telecom operators. Some telecom operators issue profiles in the form of push, which may require the use of lpac's discovery and custom IMEI function. estkme-group-lpac-c2fcf5e/docs/LINUX-DIST.md000066400000000000000000000027721504765665400205740ustar00rootroot00000000000000# Linux distributions > [!CAUTION] > > **All Linux distribution packages are unofficially maintained.** ## OpenWrt > Minimum available release: [24.10.2](https://downloads.openwrt.org/releases/24.10.2/targets/) > (Added on 2024-05-15) ```shell opkg install lpac ``` see ## Alpine > Minimum available release: [v3.20.0](https://pkgs.alpinelinux.org/packages?name=lpac&branch=v3.20) > (Release date: 2024-05-22). ```shell pkg install lpac ``` see ## Arch Linux > Need to enable [archlinuxcn repo](https://github.com/archlinuxcn/repo#readme) first > > If you want to use AUR, the package name is [lpac-git](https://aur.archlinux.org/packages/lpac-git) ```shell pacman -S lpac # or pacman -S lpac-git ``` see \ see ## Nix OS > Need to enable [NUR](https://github.com/nix-community/NUR#readme "Nix User Repository") first ```shell nix-env -i lpac ``` see ## Ubuntu and Debian/Devuan > Minimum available release: Ubuntu [14.04 Trusty Tahr](https://releases.ubuntu.com/14.04/) > (Published on 2024-09-01) see estkme-group-lpac-c2fcf5e/docs/USAGE.md000066400000000000000000000254151504765665400177770ustar00rootroot00000000000000## Usage In Linux, you need to install `pcscd`, `pcsclite` and `libcurl`. APDU and HTTP interfaces of lpac has several backends, you need to specify `$LPAC_APDU` and `$LPAC_HTTP` environment variables to interface library path. If not specified, it will use `pcsc` and `curl`. See also [environment variables](ENVVARS.md). Using `at` APDU backend need access permission to serial port (normally `/dev/ttyUSBx`). On Arch Linux, you can add yourself to `uucp` group by `sudo usermod -aG uucp $USER`. On other distro, you may need to add yourself into `dialout` group. If your serial port is not `/dev/ttyUSB0`, please use `$AT_DEVICE` to specify which one you want to use. ## CLI ### Command format ```plain lpac [subcommand] [parameters] subcommand: chip View and manage information about your eUICC card itself profile Manage the profile of your eUICC card notification Manage notifications within your eUICC card driver View libXXXXinterface info subcommand 2: Please refer to the detailed instructions below ``` ### Return value The return contents of lpac instructions are all in json format, and the returns of all instructions comply with the following format. ```jsonc { "type": "lpa", "payload": { "code": 0, "message": "success", "data": {/* .... */} } } ``` - `"type": "lpa"`: fixed content - `code`: if is 0, indicating successful execution, and other values are error codes. - `message`: is success if the operation is successful, or the error type is returned if an error occurs. - `data`: returns the returned content when the operation is successful, and is empty (but not NULL) when there is an error. ### Subcommand #### chip View the EID, default SM-DP+ server and SM-DS server of eUICC. euicc_info2 is also supported. ```plain lpac chip [parameters] subcommand: info View information about your eUICC card itself defaultsmdp Modify the default SM-DP+ server address of your eUICC card Example: lpac chip defaultsmdp purge Reset the eUICC and will clear all profiles. Use with caution! ```
Return value example ```json { "type": "lpa", "payload": { "code": 0, "message": "success", "data": { "eidValue": "[EID]", "EuiccConfiguredAddresses": { "defaultDpAddress": null, "rootDsAddress": "testrootsmds.gsma.com" }, "EUICCInfo2": { "profileVersion": "2.1.0", "svn": "2.2.0", "euiccFirmwareVer": "4.6.0", "extCardResource": { "installedApplication": 0, "freeNonVolatileMemory": 291666, "freeVolatileMemory": 5970 }, "uiccCapability": [ "usimSupport", "isimSupport", "csimSupport", "akaMilenage", "akaCave", "akaTuak128", "akaTuak256", "gbaAuthenUsim", "gbaAuthenISim", "eapClient", "javacard", "multipleUsimSupport", "multipleIsimSupport" ], "ts102241Version": "9.2.0", "globalplatformVersion": "2.3.0", "rspCapability": [ "additionalProfile", "testProfileSupport" ], "euiccCiPKIdListForVerification": [ "81370f5125d0b1d408d4c3b232e6d25e795bebfb" ], "euiccCiPKIdListForSigning": [ "81370f5125d0b1d408d4c3b232e6d25e795bebfb" ], "euiccCategory": null, "forbiddenProfilePolicyRules": [ "pprUpdateControl", "ppr1" ], "ppVersion": "0.0.1", "sasAcreditationNumber": "GI-BA-UP-0419", "certificationDataObject": { "platformLabel": "1.2.840.1234567/myPlatformLabel", "discoveryBaseURL": "https://mycompany.com/myDLOARegistrar" } } } } } ``` \* Starting from SGP.22 v2.1, `javacardVersion` is renamed to `ts102241Version` \ \*\* SGP.22 has been a typo, `sasAcreditationNumber` should be `sasAccreditationNumber`
#### profile Profile management, you can list, set alias (nickname), enable, disable, delete, download and discovery Profiles. ```plain lpac profile [parameters] subcommand: list enumerates your eUICC Profile nickname sets an alias for the specified Profile Example: lpac profile nickname enable enables the specified Profile. The RefreshFlag status is enabled by default and can be omitted. Example: lpac profile enable [1/0] disable disables the specified Profile. The RefreshFlag state is enabled by default and can be omitted. Example: lpac profile disable [1/0] delete deletes the specified Profile Example: lpac profile delete download Download profile from SM-DP server discovery Detect available profile registered on SM-DS server ``` > [!NOTE] > Some eUICC chips have trouble when enabling profile (e.g. These removable eUICC cards from ECP), try AID, ICCID, refreshFlag with 1 or 0 to find out the working way for these chips. There is no secondary confirmation for deleting a Profile, so please perform it with caution. > [!NOTE] > This function will only delete the Profile and issue a Notification, but it will not be sent automatically. You need to send it manually. ##### Download requires connection to SM-DP+ server and the following additional parameters: - `-s`: SM-DP+ server, optional, if not provided, it will try to read the default sm-dp+ attribute. - `-m`: Matching ID, activation code. optional. - `-c`: Confirmation Code, optional. - `-i`: The IMEI of the device to which Profile is to be downloaded, optional. - `-a`: LPA qrcode activation code string, e.g: `LPA:1$$`, if provided, this option takes precedence over the `-s` and `-m` options, optional. - `-p`: Interactive preview mode, optional.
Example ```bash ./lpac profile download -s rsp.truphone.com -m "QR-G-5C-1LS-1W1Z9P7" # LPA qrcode activation code string ./lpac profile download -a 'LPA:1$rsp.truphone.com$QR-G-5C-1LS-1W1Z9P7' ```
##### Discovery requires connecting to the SM-DS server to query registered profile The following parameters can be used to customize the IMEI and SM-DS server: - `-s`: SM-DS server. If not provided, it will be gsma official server "lpa.ds.gsma.com" - `-i`: IMEI of the device to which Profile is to be downloaded, optional
Return value example of lpac profile list ```json { "type": "lpa", "payload": { "code": 0, "message": "success", "data": [ { "iccid": "89353...", "isdpAid": "A0000005591010FFFFFFFF8900001000", "profileState": "disabled", "profileNickname": null, "serviceProviderName": "Vodafone IE", "profileName": "Vodafone IE eSIM", "iconType": "png", "icon": "iVBO...", "profileClass": "operational" }, { "iccid": "89012...", "isdpAid": "A0000005591010FFFFFFFF8900001100", "profileState": "disabled", "profileNickname": null, "serviceProviderName": "T-Mobile", "profileName": "CONVSIM5G_Adaptive", "iconType": "png", "icon": "iVBO...", "profileClass": "operational" }, { "iccid": "89444...", "isdpAid": "A0000005591010FFFFFFFF8900001200", "profileState": "enabled", "profileNickname": null, "serviceProviderName": "BetterRoaming", "profileName": "BetterRoaming", "iconType": "none", "icon": null, "profileClass": "operational" }, { "iccid": "89852...", "isdpAid": "A0000005591010FFFFFFFF8900001300", "profileState": "disabled", "profileNickname": null, "serviceProviderName": "Redtea Mobile", "profileName": "RedteaGO", "iconType": "none", "icon": null, "profileClass": "operational" } ] } } ``` - `iccid`: ICCID of Profile - `isdpAid`: Aid of Profile - `profileState`: State of Profile, "Enabled" or "Disabled" - `profileNickname`: Nickname of Profile - `serviceProviderName`: Telecom operators of Profile - `profileName`: Name of Profile - `iconType`: Profile icon data struct, "none", "png", "jpg" - `icon`: Profile icon data in base64 - `profileClass`: Type of Profile
#### notification Used for the management of Notifications, which are sent by telecom operators during Profile operations. You can enumerate (list), send (process), and remove (remove) Notifications. ```plain lpac notification [parameters] subcommand: list Enumerates your eUICC pending Notification list process Send Notification Example: lpac notification process remove Remove Notification Example: lpac notification remove ``` > [!NOTE] > Downstream developers or end users should process Notification as soon as possible when they exist to comply with GSMA specifications. lpac will not automatically delete the Notification after sending it. You can pass `-r` to `notification process` or you need to delete it manually.
Return value example of lpac notification list ```json { "type": "lpa", "payload": { "code": 0, "message": "success", "data": [ { "seqNumber": 178, "profileManagementOperation": "install", "notificationAddress": "rsp-eu.redteamobile.com", "iccid": "89852..." }, { "seqNumber": 215, "profileManagementOperation": "disable", "notificationAddress": "cust-005-v4-prod-atl2.gdsb.net", "iccid": "89012..." }, { "seqNumber": 216, "profileManagementOperation": "enable", "notificationAddress": "rsp.truphone.com", "iccid": "89444..." } ] } } ``` - `seqNumber`: Sequence ID - `profileManagementOperation`: Which operation generated this notification - `notificationAddress`: Profile's notification reporting server address
##### Processing requires connection to server and the following optional parameters: The following parameters can be used to customize the behavior of `notification process`: - `-a`: Process all notifications - `-r`: Automatically remove processed notifications ##### Removing supports the following optional parameters: The following parameters can be used to customize the behavior of `notification remove`: - `-a`: Remove all notifications #### driver Now, there is only one command: `lpac driver apdu list` to get the list of card readers or AT devices (AT devices are available only on the AT backend on Windows). estkme-group-lpac-c2fcf5e/docs/asn1/000077500000000000000000000000001504765665400174445ustar00rootroot00000000000000estkme-group-lpac-c2fcf5e/docs/asn1/PKIXExplicit88.asn000066400000000000000000000524501504765665400226120ustar00rootroot00000000000000PKIX1Explicit88 { iso(1) identified-organization(3) dod(6) internet(1) security(5) mechanisms(5) pkix(7) id-mod(0) id-pkix1-explicit(18) } DEFINITIONS EXPLICIT TAGS ::= BEGIN -- EXPORTS ALL -- -- IMPORTS NONE -- -- UNIVERSAL Types defined in 1993 and 1998 ASN.1 -- and required by this specification -- UniversalString ::= [UNIVERSAL 28] IMPLICIT OCTET STRING -- UniversalString is defined in ASN.1:1993 -- BMPString ::= [UNIVERSAL 30] IMPLICIT OCTET STRING -- BMPString is the subtype of UniversalString and models -- the Basic Multilingual Plane of ISO/IEC/ITU 10646-1 -- UTF8String ::= [UNIVERSAL 12] IMPLICIT OCTET STRING -- The content of this type conforms to RFC 2279. -- PKIX specific OIDs id-pkix OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) dod(6) internet(1) security(5) mechanisms(5) pkix(7) } -- PKIX arcs id-pe OBJECT IDENTIFIER ::= { id-pkix 1 } -- arc for private certificate extensions id-qt OBJECT IDENTIFIER ::= { id-pkix 2 } -- arc for policy qualifier types id-kp OBJECT IDENTIFIER ::= { id-pkix 3 } -- arc for extended key purpose OIDS id-ad OBJECT IDENTIFIER ::= { id-pkix 48 } -- arc for access descriptors -- policyQualifierIds for Internet policy qualifiers id-qt-cps OBJECT IDENTIFIER ::= { id-qt 1 } -- OID for CPS qualifier id-qt-unotice OBJECT IDENTIFIER ::= { id-qt 2 } -- OID for user notice qualifier -- access descriptor definitions id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 } id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 } id-ad-timeStamping OBJECT IDENTIFIER ::= { id-ad 3 } id-ad-caRepository OBJECT IDENTIFIER ::= { id-ad 5 } -- attribute data types Attribute ::= SEQUENCE { type AttributeType, values SET OF AttributeValue } -- at least one value is required AttributeType ::= OBJECT IDENTIFIER AttributeValue ::= ANY AttributeTypeAndValue ::= SEQUENCE { type AttributeType, value AttributeValue } -- suggested naming attributes: Definition of the following -- information object set may be augmented to meet local -- requirements. Note that deleting members of the set may -- prevent interoperability with conforming implementations. -- presented in pairs: the AttributeType followed by the -- type definition for the corresponding AttributeValue --Arc for standard naming attributes id-at OBJECT IDENTIFIER ::= { joint-iso-ccitt(2) ds(5) 4 } -- Naming attributes of type X520name id-at-name AttributeType ::= { id-at 41 } id-at-surname AttributeType ::= { id-at 4 } id-at-givenName AttributeType ::= { id-at 42 } id-at-initials AttributeType ::= { id-at 43 } id-at-generationQualifier AttributeType ::= { id-at 44 } X520name ::= CHOICE { teletexString TeletexString (SIZE (1..ub-name)), printableString PrintableString (SIZE (1..ub-name)), universalString UniversalString (SIZE (1..ub-name)), utf8String UTF8String (SIZE (1..ub-name)), bmpString BMPString (SIZE (1..ub-name)) } -- Naming attributes of type X520CommonName id-at-commonName AttributeType ::= { id-at 3 } X520CommonName ::= CHOICE { teletexString TeletexString (SIZE (1..ub-common-name)), printableString PrintableString (SIZE (1..ub-common-name)), universalString UniversalString (SIZE (1..ub-common-name)), utf8String UTF8String (SIZE (1..ub-common-name)), bmpString BMPString (SIZE (1..ub-common-name)) } -- Naming attributes of type X520LocalityName id-at-localityName AttributeType ::= { id-at 7 } X520LocalityName ::= CHOICE { teletexString TeletexString (SIZE (1..ub-locality-name)), printableString PrintableString (SIZE (1..ub-locality-name)), universalString UniversalString (SIZE (1..ub-locality-name)), utf8String UTF8String (SIZE (1..ub-locality-name)), bmpString BMPString (SIZE (1..ub-locality-name)) } -- Naming attributes of type X520StateOrProvinceName id-at-stateOrProvinceName AttributeType ::= { id-at 8 } X520StateOrProvinceName ::= CHOICE { teletexString TeletexString (SIZE (1..ub-state-name)), printableString PrintableString (SIZE (1..ub-state-name)), universalString UniversalString (SIZE (1..ub-state-name)), utf8String UTF8String (SIZE (1..ub-state-name)), bmpString BMPString (SIZE(1..ub-state-name)) } -- Naming attributes of type X520OrganizationName id-at-organizationName AttributeType ::= { id-at 10 } X520OrganizationName ::= CHOICE { teletexString TeletexString (SIZE (1..ub-organization-name)), printableString PrintableString (SIZE (1..ub-organization-name)), universalString UniversalString (SIZE (1..ub-organization-name)), utf8String UTF8String (SIZE (1..ub-organization-name)), bmpString BMPString (SIZE (1..ub-organization-name)) } -- Naming attributes of type X520OrganizationalUnitName id-at-organizationalUnitName AttributeType ::= { id-at 11 } X520OrganizationalUnitName ::= CHOICE { teletexString TeletexString (SIZE (1..ub-organizational-unit-name)), printableString PrintableString (SIZE (1..ub-organizational-unit-name)), universalString UniversalString (SIZE (1..ub-organizational-unit-name)), utf8String UTF8String (SIZE (1..ub-organizational-unit-name)), bmpString BMPString (SIZE (1..ub-organizational-unit-name)) } -- Naming attributes of type X520Title id-at-title AttributeType ::= { id-at 12 } X520Title ::= CHOICE { teletexString TeletexString (SIZE (1..ub-title)), printableString PrintableString (SIZE (1..ub-title)), universalString UniversalString (SIZE (1..ub-title)), utf8String UTF8String (SIZE (1..ub-title)), bmpString BMPString (SIZE (1..ub-title)) } -- Naming attributes of type X520dnQualifier id-at-dnQualifier AttributeType ::= { id-at 46 } X520dnQualifier ::= PrintableString -- Naming attributes of type X520countryName (digraph from IS 3166) id-at-countryName AttributeType ::= { id-at 6 } X520countryName ::= PrintableString (SIZE (2)) -- Naming attributes of type X520SerialNumber id-at-serialNumber AttributeType ::= { id-at 5 } X520SerialNumber ::= PrintableString (SIZE (1..ub-serial-number)) -- Naming attributes of type X520Pseudonym id-at-pseudonym AttributeType ::= { id-at 65 } X520Pseudonym ::= CHOICE { teletexString TeletexString (SIZE (1..ub-pseudonym)), printableString PrintableString (SIZE (1..ub-pseudonym)), universalString UniversalString (SIZE (1..ub-pseudonym)), utf8String UTF8String (SIZE (1..ub-pseudonym)), bmpString BMPString (SIZE (1..ub-pseudonym)) } -- Naming attributes of type DomainComponent (from RFC 2247) id-domainComponent AttributeType ::= { 0 9 2342 19200300 100 1 25 } DomainComponent ::= IA5String -- Legacy attributes pkcs-9 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 9 } id-emailAddress AttributeType ::= { pkcs-9 1 } EmailAddress ::= IA5String (SIZE (1..ub-emailaddress-length)) -- naming data types -- Name ::= CHOICE { -- only one possibility for now -- rdnSequence RDNSequence } RDNSequence ::= SEQUENCE OF RelativeDistinguishedName DistinguishedName ::= RDNSequence RelativeDistinguishedName ::= SET SIZE (1 .. MAX) OF AttributeTypeAndValue -- Directory string type -- DirectoryString ::= CHOICE { teletexString TeletexString (SIZE (1..MAX)), printableString PrintableString (SIZE (1..MAX)), universalString UniversalString (SIZE (1..MAX)), utf8String UTF8String (SIZE (1..MAX)), bmpString BMPString (SIZE (1..MAX)) } -- certificate and CRL specific structures begin here Certificate ::= SEQUENCE { tbsCertificate TBSCertificate, signatureAlgorithm AlgorithmIdentifier, signature BIT STRING } TBSCertificate ::= SEQUENCE { version [0] Version DEFAULT v1, serialNumber CertificateSerialNumber, signature AlgorithmIdentifier, issuer Name, validity Validity, subject Name, subjectPublicKeyInfo SubjectPublicKeyInfo, issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, -- If present, version MUST be v2 or v3 subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, -- If present, version MUST be v2 or v3 extensions [3] Extensions OPTIONAL -- If present, version MUST be v3 -- } Version ::= INTEGER { v1(0), v2(1), v3(2) } CertificateSerialNumber ::= INTEGER Validity ::= SEQUENCE { notBefore Time, notAfter Time } Time ::= CHOICE { utcTime UTCTime, generalTime GeneralizedTime } UniqueIdentifier ::= BIT STRING SubjectPublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier, subjectPublicKey BIT STRING } Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension Extension ::= SEQUENCE { extnID OBJECT IDENTIFIER, critical BOOLEAN DEFAULT FALSE, extnValue OCTET STRING } -- CRL structures CertificateList ::= SEQUENCE { tbsCertList TBSCertList, signatureAlgorithm AlgorithmIdentifier, signature BIT STRING } TBSCertList ::= SEQUENCE { version Version OPTIONAL, -- if present, MUST be v2 signature AlgorithmIdentifier, issuer Name, thisUpdate Time, nextUpdate Time OPTIONAL, revokedCertificates SEQUENCE OF SEQUENCE { userCertificate CertificateSerialNumber, revocationDate Time, crlEntryExtensions Extensions OPTIONAL -- if present, MUST be v2 } OPTIONAL, crlExtensions [0] Extensions OPTIONAL } -- if present, MUST be v2 -- Version, Time, CertificateSerialNumber, and Extensions were -- defined earlier for use in the certificate structure AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL } -- contains a value of the type -- registered for use with the -- algorithm object identifier value -- X.400 address syntax starts here ORAddress ::= SEQUENCE { built-in-standard-attributes BuiltInStandardAttributes, built-in-domain-defined-attributes BuiltInDomainDefinedAttributes OPTIONAL, -- see also teletex-domain-defined-attributes extension-attributes ExtensionAttributes OPTIONAL } -- Built-in Standard Attributes BuiltInStandardAttributes ::= SEQUENCE { country-name CountryName OPTIONAL, administration-domain-name AdministrationDomainName OPTIONAL, network-address [0] IMPLICIT NetworkAddress OPTIONAL, -- see also extended-network-address terminal-identifier [1] IMPLICIT TerminalIdentifier OPTIONAL, private-domain-name [2] PrivateDomainName OPTIONAL, organization-name [3] IMPLICIT OrganizationName OPTIONAL, -- see also teletex-organization-name numeric-user-identifier [4] IMPLICIT NumericUserIdentifier OPTIONAL, personal-name [5] IMPLICIT PersonalName OPTIONAL, -- see also teletex-personal-name organizational-unit-names [6] IMPLICIT OrganizationalUnitNames OPTIONAL } -- see also teletex-organizational-unit-names CountryName ::= [APPLICATION 1] CHOICE { x121-dcc-code NumericString (SIZE (ub-country-name-numeric-length)), iso-3166-alpha2-code PrintableString (SIZE (ub-country-name-alpha-length)) } AdministrationDomainName ::= [APPLICATION 2] CHOICE { numeric NumericString (SIZE (0..ub-domain-name-length)), printable PrintableString (SIZE (0..ub-domain-name-length)) } NetworkAddress ::= X121Address -- see also extended-network-address X121Address ::= NumericString (SIZE (1..ub-x121-address-length)) TerminalIdentifier ::= PrintableString (SIZE (1..ub-terminal-id-length)) PrivateDomainName ::= CHOICE { numeric NumericString (SIZE (1..ub-domain-name-length)), printable PrintableString (SIZE (1..ub-domain-name-length)) } OrganizationName ::= PrintableString (SIZE (1..ub-organization-name-length)) -- see also teletex-organization-name NumericUserIdentifier ::= NumericString (SIZE (1..ub-numeric-user-id-length)) PersonalName ::= SET { surname [0] IMPLICIT PrintableString (SIZE (1..ub-surname-length)), given-name [1] IMPLICIT PrintableString (SIZE (1..ub-given-name-length)) OPTIONAL, initials [2] IMPLICIT PrintableString (SIZE (1..ub-initials-length)) OPTIONAL, generation-qualifier [3] IMPLICIT PrintableString (SIZE (1..ub-generation-qualifier-length)) OPTIONAL } -- see also teletex-personal-name OrganizationalUnitNames ::= SEQUENCE SIZE (1..ub-organizational-units) OF OrganizationalUnitName -- see also teletex-organizational-unit-names OrganizationalUnitName ::= PrintableString (SIZE (1..ub-organizational-unit-name-length)) -- Built-in Domain-defined Attributes BuiltInDomainDefinedAttributes ::= SEQUENCE SIZE (1..ub-domain-defined-attributes) OF BuiltInDomainDefinedAttribute BuiltInDomainDefinedAttribute ::= SEQUENCE { type PrintableString (SIZE (1..ub-domain-defined-attribute-type-length)), value PrintableString (SIZE (1..ub-domain-defined-attribute-value-length)) } -- Extension Attributes ExtensionAttributes ::= SET SIZE (1..ub-extension-attributes) OF ExtensionAttribute ExtensionAttribute ::= SEQUENCE { extension-attribute-type [0] IMPLICIT INTEGER (0..ub-extension-attributes), extension-attribute-value [1] ANY DEFINED BY extension-attribute-type } -- Extension types and attribute values common-name INTEGER ::= 1 CommonName ::= PrintableString (SIZE (1..ub-common-name-length)) teletex-common-name INTEGER ::= 2 TeletexCommonName ::= TeletexString (SIZE (1..ub-common-name-length)) teletex-organization-name INTEGER ::= 3 TeletexOrganizationName ::= TeletexString (SIZE (1..ub-organization-name-length)) teletex-personal-name INTEGER ::= 4 TeletexPersonalName ::= SET { surname [0] IMPLICIT TeletexString (SIZE (1..ub-surname-length)), given-name [1] IMPLICIT TeletexString (SIZE (1..ub-given-name-length)) OPTIONAL, initials [2] IMPLICIT TeletexString (SIZE (1..ub-initials-length)) OPTIONAL, generation-qualifier [3] IMPLICIT TeletexString (SIZE (1..ub-generation-qualifier-length)) OPTIONAL } teletex-organizational-unit-names INTEGER ::= 5 TeletexOrganizationalUnitNames ::= SEQUENCE SIZE (1..ub-organizational-units) OF TeletexOrganizationalUnitName TeletexOrganizationalUnitName ::= TeletexString (SIZE (1..ub-organizational-unit-name-length)) pds-name INTEGER ::= 7 PDSName ::= PrintableString (SIZE (1..ub-pds-name-length)) physical-delivery-country-name INTEGER ::= 8 PhysicalDeliveryCountryName ::= CHOICE { x121-dcc-code NumericString (SIZE (ub-country-name-numeric-length)), iso-3166-alpha2-code PrintableString (SIZE (ub-country-name-alpha-length)) } postal-code INTEGER ::= 9 PostalCode ::= CHOICE { numeric-code NumericString (SIZE (1..ub-postal-code-length)), printable-code PrintableString (SIZE (1..ub-postal-code-length)) } physical-delivery-office-name INTEGER ::= 10 PhysicalDeliveryOfficeName ::= PDSParameter physical-delivery-office-number INTEGER ::= 11 PhysicalDeliveryOfficeNumber ::= PDSParameter extension-OR-address-components INTEGER ::= 12 ExtensionORAddressComponents ::= PDSParameter physical-delivery-personal-name INTEGER ::= 13 PhysicalDeliveryPersonalName ::= PDSParameter physical-delivery-organization-name INTEGER ::= 14 PhysicalDeliveryOrganizationName ::= PDSParameter extension-physical-delivery-address-components INTEGER ::= 15 ExtensionPhysicalDeliveryAddressComponents ::= PDSParameter unformatted-postal-address INTEGER ::= 16 UnformattedPostalAddress ::= SET { printable-address SEQUENCE SIZE (1..ub-pds-physical-address-lines) OF PrintableString (SIZE (1..ub-pds-parameter-length)) OPTIONAL, teletex-string TeletexString (SIZE (1..ub-unformatted-address-length)) OPTIONAL } street-address INTEGER ::= 17 StreetAddress ::= PDSParameter post-office-box-address INTEGER ::= 18 PostOfficeBoxAddress ::= PDSParameter poste-restante-address INTEGER ::= 19 PosteRestanteAddress ::= PDSParameter unique-postal-name INTEGER ::= 20 UniquePostalName ::= PDSParameter local-postal-attributes INTEGER ::= 21 LocalPostalAttributes ::= PDSParameter PDSParameter ::= SET { printable-string PrintableString (SIZE(1..ub-pds-parameter-length)) OPTIONAL, teletex-string TeletexString (SIZE(1..ub-pds-parameter-length)) OPTIONAL } extended-network-address INTEGER ::= 22 ExtendedNetworkAddress ::= CHOICE { e163-4-address SEQUENCE { number [0] IMPLICIT NumericString (SIZE (1..ub-e163-4-number-length)), sub-address [1] IMPLICIT NumericString (SIZE (1..ub-e163-4-sub-address-length)) OPTIONAL }, psap-address [0] IMPLICIT PresentationAddress } PresentationAddress ::= SEQUENCE { pSelector [0] EXPLICIT OCTET STRING OPTIONAL, sSelector [1] EXPLICIT OCTET STRING OPTIONAL, tSelector [2] EXPLICIT OCTET STRING OPTIONAL, nAddresses [3] EXPLICIT SET SIZE (1..MAX) OF OCTET STRING } terminal-type INTEGER ::= 23 TerminalType ::= INTEGER { telex (3), teletex (4), g3-facsimile (5), g4-facsimile (6), ia5-terminal (7), videotex (8) } -- Extension Domain-defined Attributes teletex-domain-defined-attributes INTEGER ::= 6 TeletexDomainDefinedAttributes ::= SEQUENCE SIZE (1..ub-domain-defined-attributes) OF TeletexDomainDefinedAttribute TeletexDomainDefinedAttribute ::= SEQUENCE { type TeletexString (SIZE (1..ub-domain-defined-attribute-type-length)), value TeletexString (SIZE (1..ub-domain-defined-attribute-value-length)) } -- specifications of Upper Bounds MUST be regarded as mandatory -- from Annex B of ITU-T X.411 Reference Definition of MTS Parameter -- Upper Bounds -- Upper Bounds ub-name INTEGER ::= 32768 ub-common-name INTEGER ::= 64 ub-locality-name INTEGER ::= 128 ub-state-name INTEGER ::= 128 ub-organization-name INTEGER ::= 64 ub-organizational-unit-name INTEGER ::= 64 ub-title INTEGER ::= 64 ub-serial-number INTEGER ::= 64 ub-match INTEGER ::= 128 ub-emailaddress-length INTEGER ::= 255 ub-common-name-length INTEGER ::= 64 ub-country-name-alpha-length INTEGER ::= 2 ub-country-name-numeric-length INTEGER ::= 3 ub-domain-defined-attributes INTEGER ::= 4 ub-domain-defined-attribute-type-length INTEGER ::= 8 ub-domain-defined-attribute-value-length INTEGER ::= 128 ub-domain-name-length INTEGER ::= 16 ub-extension-attributes INTEGER ::= 256 ub-e163-4-number-length INTEGER ::= 15 ub-e163-4-sub-address-length INTEGER ::= 40 ub-generation-qualifier-length INTEGER ::= 3 ub-given-name-length INTEGER ::= 16 ub-initials-length INTEGER ::= 5 ub-integer-options INTEGER ::= 256 ub-numeric-user-id-length INTEGER ::= 32 ub-organization-name-length INTEGER ::= 64 ub-organizational-unit-name-length INTEGER ::= 32 ub-organizational-units INTEGER ::= 4 ub-pds-name-length INTEGER ::= 16 ub-pds-parameter-length INTEGER ::= 30 ub-pds-physical-address-lines INTEGER ::= 6 ub-postal-code-length INTEGER ::= 16 ub-pseudonym INTEGER ::= 128 ub-surname-length INTEGER ::= 40 ub-terminal-id-length INTEGER ::= 24 ub-unformatted-address-length INTEGER ::= 180 ub-x121-address-length INTEGER ::= 16 -- Note - upper bounds on string types, such as TeletexString, are -- measured in characters. Excepting PrintableString or IA5String, a -- significantly greater number of octets will be required to hold -- such a value. As a minimum, 16 octets, or twice the specified -- upper bound, whichever is the larger, should be allowed for -- TeletexString. For UTF8String or UniversalString at least four -- times the upper bound should be allowed. ENDestkme-group-lpac-c2fcf5e/docs/asn1/PKIXImplicit88.asn000066400000000000000000000247671504765665400226150ustar00rootroot00000000000000 -- -- ASN.1 module found by ./crfc2asn1.pl in rfc3280.txt at line 5850 -- PKIX1Implicit88 { iso(1) identified-organization(3) dod(6) internet(1) security(5) mechanisms(5) pkix(7) id-mod(0) id-pkix1-implicit(19) } DEFINITIONS IMPLICIT TAGS ::= BEGIN -- EXPORTS ALL -- IMPORTS id-pe, id-kp, id-qt-unotice, id-qt-cps, ORAddress, Name, RelativeDistinguishedName, CertificateSerialNumber, Attribute, DirectoryString FROM PKIX1Explicit88 { iso(1) identified-organization(3) dod(6) internet(1) security(5) mechanisms(5) pkix(7) id-mod(0) id-pkix1-explicit(18) }; -- ISO arc for standard certificate and CRL extensions id-ce OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 29} -- authority key identifier OID and syntax id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 } AuthorityKeyIdentifier ::= SEQUENCE { keyIdentifier [0] KeyIdentifier OPTIONAL, authorityCertIssuer [1] GeneralNames OPTIONAL, authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } -- authorityCertIssuer and authorityCertSerialNumber MUST both -- be present or both be absent KeyIdentifier ::= OCTET STRING -- subject key identifier OID and syntax id-ce-subjectKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 14 } SubjectKeyIdentifier ::= KeyIdentifier -- key usage extension OID and syntax id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 } KeyUsage ::= BIT STRING { digitalSignature (0), nonRepudiation (1), keyEncipherment (2), dataEncipherment (3), keyAgreement (4), keyCertSign (5), cRLSign (6), encipherOnly (7), decipherOnly (8) } -- private key usage period extension OID and syntax id-ce-privateKeyUsagePeriod OBJECT IDENTIFIER ::= { id-ce 16 } PrivateKeyUsagePeriod ::= SEQUENCE { notBefore [0] GeneralizedTime OPTIONAL, notAfter [1] GeneralizedTime OPTIONAL } -- either notBefore or notAfter MUST be present -- certificate policies extension OID and syntax id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 } anyPolicy OBJECT IDENTIFIER ::= { id-ce-certificatePolicies 0 } CertificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation PolicyInformation ::= SEQUENCE { policyIdentifier CertPolicyId, policyQualifiers SEQUENCE SIZE (1..MAX) OF PolicyQualifierInfo OPTIONAL } CertPolicyId ::= OBJECT IDENTIFIER PolicyQualifierInfo ::= SEQUENCE { policyQualifierId PolicyQualifierId, qualifier ANY DEFINED BY policyQualifierId } -- Implementations that recognize additional policy qualifiers MUST -- augment the following definition for PolicyQualifierId PolicyQualifierId ::= OBJECT IDENTIFIER -- ( id-qt-cps | id-qt-unotice ) -- CPS pointer qualifier CPSuri ::= IA5String -- user notice qualifier UserNotice ::= SEQUENCE { noticeRef NoticeReference OPTIONAL, explicitText DisplayText OPTIONAL} NoticeReference ::= SEQUENCE { organization DisplayText, noticeNumbers SEQUENCE OF INTEGER } DisplayText ::= CHOICE { ia5String IA5String (SIZE (1..200)), visibleString VisibleString (SIZE (1..200)), bmpString BMPString (SIZE (1..200)), utf8String UTF8String (SIZE (1..200)) } -- policy mapping extension OID and syntax id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 } PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE { issuerDomainPolicy CertPolicyId, subjectDomainPolicy CertPolicyId } -- subject alternative name extension OID and syntax id-ce-subjectAltName OBJECT IDENTIFIER ::= { id-ce 17 } SubjectAltName ::= GeneralNames GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName GeneralName ::= CHOICE { otherName [0] AnotherName, rfc822Name [1] IA5String, dNSName [2] IA5String, x400Address [3] ORAddress, directoryName [4] Name, ediPartyName [5] EDIPartyName, uniformResourceIdentifier [6] IA5String, iPAddress [7] OCTET STRING, registeredID [8] OBJECT IDENTIFIER } -- AnotherName replaces OTHER-NAME ::= TYPE-IDENTIFIER, as -- TYPE-IDENTIFIER is not supported in the '88 ASN.1 syntax AnotherName ::= SEQUENCE { type-id OBJECT IDENTIFIER, value [0] EXPLICIT ANY DEFINED BY type-id } EDIPartyName ::= SEQUENCE { nameAssigner [0] DirectoryString OPTIONAL, partyName [1] DirectoryString } -- issuer alternative name extension OID and syntax id-ce-issuerAltName OBJECT IDENTIFIER ::= { id-ce 18 } IssuerAltName ::= GeneralNames id-ce-subjectDirectoryAttributes OBJECT IDENTIFIER ::= { id-ce 9 } SubjectDirectoryAttributes ::= SEQUENCE SIZE (1..MAX) OF Attribute -- basic constraints extension OID and syntax id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 } BasicConstraints ::= SEQUENCE { cA BOOLEAN DEFAULT FALSE, pathLenConstraint INTEGER (0..MAX) OPTIONAL } -- name constraints extension OID and syntax id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 } NameConstraints ::= SEQUENCE { permittedSubtrees [0] GeneralSubtrees OPTIONAL, excludedSubtrees [1] GeneralSubtrees OPTIONAL } GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree GeneralSubtree ::= SEQUENCE { base GeneralName, minimum [0] BaseDistance DEFAULT 0, maximum [1] BaseDistance OPTIONAL } BaseDistance ::= INTEGER (0..MAX) -- policy constraints extension OID and syntax id-ce-policyConstraints OBJECT IDENTIFIER ::= { id-ce 36 } PolicyConstraints ::= SEQUENCE { requireExplicitPolicy [0] SkipCerts OPTIONAL, inhibitPolicyMapping [1] SkipCerts OPTIONAL } SkipCerts ::= INTEGER (0..MAX) -- CRL distribution points extension OID and syntax id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= {id-ce 31} CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint DistributionPoint ::= SEQUENCE { distributionPoint [0] DistributionPointName OPTIONAL, reasons [1] ReasonFlags OPTIONAL, cRLIssuer [2] GeneralNames OPTIONAL } DistributionPointName ::= CHOICE { fullName [0] GeneralNames, nameRelativeToCRLIssuer [1] RelativeDistinguishedName } ReasonFlags ::= BIT STRING { unused (0), keyCompromise (1), cACompromise (2), affiliationChanged (3), superseded (4), cessationOfOperation (5), certificateHold (6), privilegeWithdrawn (7), aACompromise (8) } -- extended key usage extension OID and syntax id-ce-extKeyUsage OBJECT IDENTIFIER ::= {id-ce 37} ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId KeyPurposeId ::= OBJECT IDENTIFIER -- permit unspecified key uses anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 } -- extended key purpose OIDs id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 } id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 } id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 } id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 } id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 } id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 } -- inhibit any policy OID and syntax id-ce-inhibitAnyPolicy OBJECT IDENTIFIER ::= { id-ce 54 } InhibitAnyPolicy ::= SkipCerts -- freshest (delta)CRL extension OID and syntax id-ce-freshestCRL OBJECT IDENTIFIER ::= { id-ce 46 } FreshestCRL ::= CRLDistributionPoints -- authority info access id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 } AuthorityInfoAccessSyntax ::= SEQUENCE SIZE (1..MAX) OF AccessDescription AccessDescription ::= SEQUENCE { accessMethod OBJECT IDENTIFIER, accessLocation GeneralName } -- subject info access id-pe-subjectInfoAccess OBJECT IDENTIFIER ::= { id-pe 11 } SubjectInfoAccessSyntax ::= SEQUENCE SIZE (1..MAX) OF AccessDescription -- CRL number extension OID and syntax id-ce-cRLNumber OBJECT IDENTIFIER ::= { id-ce 20 } CRLNumber ::= INTEGER (0..MAX) -- issuing distribution point extension OID and syntax id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 } IssuingDistributionPoint ::= SEQUENCE { distributionPoint [0] DistributionPointName OPTIONAL, onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE, onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE, onlySomeReasons [3] ReasonFlags OPTIONAL, indirectCRL [4] BOOLEAN DEFAULT FALSE, onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE } id-ce-deltaCRLIndicator OBJECT IDENTIFIER ::= { id-ce 27 } BaseCRLNumber ::= CRLNumber -- CRL reasons extension OID and syntax id-ce-cRLReasons OBJECT IDENTIFIER ::= { id-ce 21 } CRLReason ::= ENUMERATED { unspecified (0), keyCompromise (1), cACompromise (2), affiliationChanged (3), superseded (4), cessationOfOperation (5), certificateHold (6), removeFromCRL (8), privilegeWithdrawn (9), aACompromise (10) } -- certificate issuer CRL entry extension OID and syntax id-ce-certificateIssuer OBJECT IDENTIFIER ::= { id-ce 29 } CertificateIssuer ::= GeneralNames -- hold instruction extension OID and syntax id-ce-holdInstructionCode OBJECT IDENTIFIER ::= { id-ce 23 } HoldInstructionCode ::= OBJECT IDENTIFIER -- ANSI x9 holdinstructions -- ANSI x9 arc holdinstruction arc holdInstruction OBJECT IDENTIFIER ::= {joint-iso-itu-t(2) member-body(2) us(840) x9cm(10040) 2} -- ANSI X9 holdinstructions referenced by this standard id-holdinstruction-none OBJECT IDENTIFIER ::= {holdInstruction 1} -- deprecated id-holdinstruction-callissuer OBJECT IDENTIFIER ::= {holdInstruction 2} id-holdinstruction-reject OBJECT IDENTIFIER ::= {holdInstruction 3} -- invalidity date CRL entry extension OID and syntax id-ce-invalidityDate OBJECT IDENTIFIER ::= { id-ce 24 } InvalidityDate ::= GeneralizedTime ENDestkme-group-lpac-c2fcf5e/docs/asn1/rsp.asn000066400000000000000000001015121504765665400207530ustar00rootroot00000000000000RSPDefinitions {joint-iso-itu-t(2) international-organizations(23) gsma(146) rsp(1) spec-version(1) version-two(2)} DEFINITIONS AUTOMATIC TAGS EXTENSIBILITY IMPLIED ::= BEGIN IMPORTS Certificate, CertificateList, Time FROM PKIX1Explicit88 {iso(1) identified-organization(3) dod(6) internet(1) security(5) mechanisms(5) pkix(7) id-mod(0) id-pkix1-explicit(18)} SubjectKeyIdentifier FROM PKIX1Implicit88 {iso(1) identified-organization(3) dod(6) internet(1) security(5) mechanisms(5) pkix(7) id-mod(0) id-pkix1-implicit(19)}; id-rsp OBJECT IDENTIFIER ::= {joint-iso-itu-t(2) international-organizations(23) gsma(146) rsp(1)} -- Basic types, for size constraints Octet8 ::= OCTET STRING (SIZE(8)) Octet4 ::= OCTET STRING (SIZE(4)) Octet16 ::= OCTET STRING (SIZE(16)) OctetTo16 ::= OCTET STRING (SIZE(1..16)) Octet32 ::= OCTET STRING (SIZE(32)) Octet1 ::= OCTET STRING(SIZE(1)) Octet2 ::= OCTET STRING (SIZE(2)) VersionType ::= OCTET STRING(SIZE(3)) -- major/minor/revision version are coded as binary value on byte 1/2/3, e.g. '02 00 0C' for v2.0.12. -- If revision is not used (e.g. v2.1), byte 3 SHALL be set to '00'. Iccid ::= [APPLICATION 26] OCTET STRING (SIZE(10)) -- ICCID as coded in EFiccid, corresponding tag is '5A' RemoteOpId ::= [2] INTEGER {installBoundProfilePackage(1)} TransactionId ::= OCTET STRING (SIZE(1..16)) -- Definition of EUICCInfo1 -------------------------- GetEuiccInfo1Request ::= [32] SEQUENCE { -- Tag 'BF20' } EUICCInfo1 ::= [32] SEQUENCE { -- Tag 'BF20' svn [2] VersionType, -- GSMA SGP.22 version supported (SVN) euiccCiPKIdListForVerification [9] SEQUENCE OF SubjectKeyIdentifier, -- List of CI Public Key Identifiers supported on the eUICC for signature verification euiccCiPKIdListForSigning [10] SEQUENCE OF SubjectKeyIdentifier -- List of CI Public Key Identifier supported on the eUICC for signature creation } -- Definition of EUICCInfo2 -------------------------- GetEuiccInfo2Request ::= [34] SEQUENCE { -- Tag 'BF22' } EUICCInfo2 ::= [34] SEQUENCE { -- Tag 'BF22' profileVersion [1] VersionType, -- SIMAlliance Profile package version supported svn [2] VersionType, -- GSMA SGP.22 version supported (SVN) euiccFirmwareVer [3] VersionType, -- eUICC Firmware version extCardResource [4] OCTET STRING, -- Extended Card Resource Information according to ETSI TS 102 226 uiccCapability [5] UICCCapability, ts102241Version [6] VersionType OPTIONAL, globalplatformVersion [7] VersionType OPTIONAL, rspCapability [8] RspCapability, euiccCiPKIdListForVerification [9] SEQUENCE OF SubjectKeyIdentifier, -- List of CI Public Key Identifiers supported on the eUICC for signature verification euiccCiPKIdListForSigning [10] SEQUENCE OF SubjectKeyIdentifier, -- List of CI Public Key Identifier supported on the eUICC for signature creation euiccCategory [11] INTEGER { other(0), basicEuicc(1), mediumEuicc(2), contactlessEuicc(3) } OPTIONAL, forbiddenProfilePolicyRules [25] PprIds OPTIONAL, -- Tag '99' ppVersion VersionType, -- Protection Profile version sasAcreditationNumber UTF8String (SIZE(0..64)), certificationDataObject [12] CertificationDataObject OPTIONAL } -- Definition of RspCapability RspCapability ::= BIT STRING { additionalProfile(0), -- at least one more Profile can be installed crlSupport(1), -- CRL rpmSupport(2), -- Remote Profile Management testProfileSupport (3), -- support for test profile deviceInfoExtensibilitySupport (4) -- support for ASN.1 extensibility in the Device Info } -- Definition of CertificationDataObject CertificationDataObject ::= SEQUENCE { platformLabel UTF8String, -- Platform_Label as defined in GlobalPlatform DLOA specification [57] discoveryBaseURL UTF8String -- Discovery Base URL of the SE default DLOA Registrar as defined in GlobalPlatform DLOA specification [57] } CertificateInfo ::= BIT STRING { reserved(0), -- eUICC has a CERT.EUICC.ECDSA in GlobalPlatform format. The use of this bit is deprecated. certSigningX509(1), -- eUICC has a CERT.EUICC.ECDSA in X.509 format rfu2(2), rfu3(3), reserved2(4), -- Handling of Certificate in GlobalPlatform format. The use of this bit is deprecated. certVerificationX509(5)-- Handling of Certificate in X.509 format } -- Definition of UICCCapability UICCCapability ::= BIT STRING { /* Sequence is derived from ServicesList[] defined in SIMalliance PEDefinitions*/ contactlessSupport(0), -- Contactless (SWP, HCI and associated APIs) usimSupport(1), -- USIM as defined by 3GPP isimSupport(2), -- ISIM as defined by 3GPP csimSupport(3), -- CSIM as defined by 3GPP2 akaMilenage(4), -- Milenage as AKA algorithm akaCave(5), -- CAVE as authentication algorithm akaTuak128(6), -- TUAK as AKA algorithm with 128 bit key length akaTuak256(7), -- TUAK as AKA algorithm with 256 bit key length rfu1(8), -- reserved for further algorithms rfu2(9), -- reserved for further algorithms gbaAuthenUsim(10), -- GBA authentication in the context of USIM gbaAuthenISim(11), -- GBA authentication in the context of ISIM mbmsAuthenUsim(12), -- MBMS authentication in the context of USIM eapClient(13), -- EAP client javacard(14), -- Javacard support multos(15), -- Multos support multipleUsimSupport(16), -- Multiple USIM applications are supported within the same Profile multipleIsimSupport(17), -- Multiple ISIM applications are supported within the same Profile multipleCsimSupport(18), -- Multiple CSIM applications are supported within the same Profile berTlvFileSupport(19), -- BER TLV files dfLinkSupport(20), -- Linked Directory Files catTp(21), -- Support of CAT TP getIdentity(22), -- Support of the GET IDENTITY command as defined in ETSI TS 102 221 [6] profile-a-x25519(23), -- Support of ECIES Profile A as defined in 3GPP TS 33.501 [87] profile-b-p256(24), -- Support of ECIES Profile B as defined in 3GPP TS 33.501 [87] suciCalculatorApi(25) -- Support of the associated API for SUCI derivation as defined in 3GPP 31.130 [88] } -- Definition of DeviceInfo DeviceInfo ::= SEQUENCE { tac Octet4, deviceCapabilities DeviceCapabilities, imei Octet8 OPTIONAL } DeviceCapabilities ::= SEQUENCE { -- Highest fully supported release for each definition -- The device SHALL set all the capabilities it supports gsmSupportedRelease VersionType OPTIONAL, utranSupportedRelease VersionType OPTIONAL, cdma2000onexSupportedRelease VersionType OPTIONAL, cdma2000hrpdSupportedRelease VersionType OPTIONAL, cdma2000ehrpdSupportedRelease VersionType OPTIONAL, eutranEpcSupportedRelease VersionType OPTIONAL, contactlessSupportedRelease VersionType OPTIONAL, rspCrlSupportedVersion VersionType OPTIONAL, nrEpcSupportedRelease VersionType OPTIONAL, nr5gcSupportedRelease VersionType OPTIONAL, eutran5gcSupportedRelease VersionType OPTIONAL } ProfileInfoListRequest ::= [45] SEQUENCE { -- Tag 'BF2D' searchCriteria [0] CHOICE { isdpAid [APPLICATION 15] OctetTo16, -- AID of the ISD-P, tag '4F' iccid Iccid, -- ICCID, tag '5A' profileClass [21] ProfileClass -- Tag '95' } OPTIONAL, tagList [APPLICATION 28] OCTET STRING OPTIONAL -- tag '5C' } -- Definition of ProfileInfoList ProfileInfoListResponse ::= [45] CHOICE { -- Tag 'BF2D' profileInfoListOk SEQUENCE OF ProfileInfo, profileInfoListError ProfileInfoListError } ProfileInfo ::= [PRIVATE 3] SEQUENCE { -- Tag 'E3' iccid Iccid OPTIONAL, isdpAid [APPLICATION 15] OctetTo16 OPTIONAL, -- AID of the ISD-P containing the Profile, tag '4F' profileState [112] ProfileState OPTIONAL, -- Tag '9F70' profileNickname [16] UTF8String (SIZE(0..64)) OPTIONAL, -- Tag '90' serviceProviderName [17] UTF8String (SIZE(0..32)) OPTIONAL, -- Tag '91' profileName [18] UTF8String (SIZE(0..64)) OPTIONAL, -- Tag '92' iconType [19] IconType OPTIONAL, -- Tag '93' icon [20] OCTET STRING (SIZE(0..1024)) OPTIONAL, -- Tag '94', see condition in ES10c:GetProfilesInfo profileClass [21] ProfileClass OPTIONAL, -- Tag '95' notificationConfigurationInfo [22] SEQUENCE OF NotificationConfigurationInformation OPTIONAL, -- Tag 'B6' profileOwner [23] OperatorId OPTIONAL, -- Tag 'B7' dpProprietaryData [24] DpProprietaryData OPTIONAL, -- Tag 'B8' profilePolicyRules [25] PprIds OPTIONAL, -- Tag '99' refArDo [118] SEQUENCE OF RefArDo OPTIONAL -- Tag 'BF76' } RefArDo ::= [PRIVATE 2] SEQUENCE { -- Tag 'E2' refDo [PRIVATE 1] SEQUENCE { -- Tag 'E1' deviceAppIdRefDo [PRIVATE 1] OCTET STRING (SIZE(20|32)), -- Tag 'C1' pkgRefDo [PRIVATE 10] OCTET STRING (SIZE(0..127)) OPTIONAL -- Tag 'CA' }, arDo [PRIVATE 3] SEQUENCE { -- Tag 'E3' permArDo [PRIVATE 27] OCTET STRING (SIZE(8)) -- Tag 'DB' } } PprIds ::= BIT STRING {-- Definition of Profile Policy Rules identifiers pprUpdateControl(0), -- defines how to update PPRs via ES6 ppr1(1), -- Indicator for PPR1 'Disabling of this Profile is not allowed' ppr2(2) -- Indicator for PPR2 'Deletion of this Profile is not allowed' } OperatorId ::= SEQUENCE { mccMnc OCTET STRING (SIZE(3)), -- MCC and MNC coded as defined in 3GPP TS 24.008 [32] gid1 OCTET STRING OPTIONAL, -- referring to content of EF GID1 (file identifier '6F3E') as defined in 3GPP TS 31.102 [54] gid2 OCTET STRING OPTIONAL -- referring to content of EF GID2 (file identifier '6F3F') as defined in 3GPP TS 31.102 [54] } ProfileInfoListError ::= INTEGER {incorrectInputValues(1), undefinedError(127)} -- Definition of StoreMetadata request StoreMetadataRequest ::= [37] SEQUENCE { -- Tag 'BF25' iccid Iccid, serviceProviderName [17] UTF8String (SIZE(0..32)), -- Tag '91' profileName [18] UTF8String (SIZE(0..64)), -- Tag '92' (corresponds to 'Short Description' defined in SGP.21 [2]) iconType [19] IconType OPTIONAL, -- Tag '93' (JPG or PNG) icon [20] OCTET STRING (SIZE(0..1024)) OPTIONAL, -- Tag '94'(Data of the icon. Size 64 x 64 pixel. This field SHALL only be present if iconType is present) profileClass [21] ProfileClass DEFAULT operational, -- Tag '95' notificationConfigurationInfo [22] SEQUENCE OF NotificationConfigurationInformation OPTIONAL, profileOwner [23] OperatorId OPTIONAL, -- Tag 'B7' profilePolicyRules [25] PprIds OPTIONAL -- Tag '99' } NotificationEvent ::= BIT STRING { notificationInstall (0), notificationEnable(1), notificationDisable(2), notificationDelete(3) } NotificationConfigurationInformation ::= SEQUENCE { profileManagementOperation NotificationEvent, notificationAddress UTF8String -- FQDN to forward the notification } IconType ::= INTEGER {jpg(0), png(1)} ProfileState ::= INTEGER {disabled(0), enabled(1)} ProfileClass ::= INTEGER {test(0), provisioning(1), operational(2)} -- Definition of UpdateMetadata request UpdateMetadataRequest ::= [42] SEQUENCE { -- Tag 'BF2A' serviceProviderName [17] UTF8String (SIZE(0..32)) OPTIONAL, -- Tag '91' profileName [18] UTF8String (SIZE(0..64)) OPTIONAL, -- Tag '92' iconType [19] IconType OPTIONAL, -- Tag '93' icon [20] OCTET STRING (SIZE(0..1024)) OPTIONAL, -- Tag '94' profilePolicyRules [25] PprIds OPTIONAL -- Tag '99' } -- Definition of data objects for command PrepareDownload ------------------------- PrepareDownloadRequest ::= [33] SEQUENCE { -- Tag 'BF21' smdpSigned2 SmdpSigned2, -- Signed information smdpSignature2 [APPLICATION 55] OCTET STRING, -- DP_Sign1, tag '5F37' hashCc Octet32 OPTIONAL, -- Hash of confirmation code smdpCertificate Certificate -- CERT.DPpb.ECDSA } SmdpSigned2 ::= SEQUENCE { transactionId [0] TransactionId, -- The TransactionID generated by the SM-DP+ ccRequiredFlag BOOLEAN, --Indicates if the Confirmation Code is required bppEuiccOtpk [APPLICATION 73] OCTET STRING OPTIONAL -- otPK.EUICC.ECKA already used for binding the BPP, tag '5F49' } PrepareDownloadResponse ::= [33] CHOICE { -- Tag 'BF21' downloadResponseOk PrepareDownloadResponseOk, downloadResponseError PrepareDownloadResponseError } PrepareDownloadResponseOk ::= SEQUENCE { euiccSigned2 EUICCSigned2, -- Signed information euiccSignature2 [APPLICATION 55] OCTET STRING -- tag '5F37' } EUICCSigned2 ::= SEQUENCE { transactionId [0] TransactionId, euiccOtpk [APPLICATION 73] OCTET STRING, -- otPK.EUICC.ECKA, tag '5F49' hashCc Octet32 OPTIONAL -- Hash of confirmation code } PrepareDownloadResponseError ::= SEQUENCE { transactionId [0] TransactionId, downloadErrorCode DownloadErrorCode } DownloadErrorCode ::= INTEGER {invalidCertificate(1), invalidSignature(2), unsupportedCurve(3), noSessionContext(4), invalidTransactionId(5), undefinedError(127)} -- Definition of data objects for command AuthenticateServer-------------------- AuthenticateServerRequest ::= [56] SEQUENCE { -- Tag 'BF38' serverSigned1 ServerSigned1, -- Signed information serverSignature1 [APPLICATION 55] OCTET STRING, -- tag ‘5F37’ euiccCiPKIdToBeUsed SubjectKeyIdentifier, -- CI Public Key Identifier to be used serverCertificate Certificate, -- RSP Server Certificate CERT.XXauth.ECDSA ctxParams1 CtxParams1 } ServerSigned1 ::= SEQUENCE { transactionId [0] TransactionId, -- The Transaction ID generated by the RSP Server euiccChallenge [1] Octet16, -- The eUICC Challenge serverAddress [3] UTF8String, -- The RSP Server address serverChallenge [4] Octet16 -- The RSP Server Challenge } CtxParams1 ::= CHOICE { ctxParamsForCommonAuthentication CtxParamsForCommonAuthentication -- New contextual data objects MAY be defined for extensibility } CtxParamsForCommonAuthentication ::= SEQUENCE { matchingId UTF8String OPTIONAL,-- The MatchingId could be the Activation code token or EventID or empty deviceInfo DeviceInfo -- The Device information } AuthenticateServerResponse ::= [56] CHOICE { -- Tag 'BF38' authenticateResponseOk AuthenticateResponseOk, authenticateResponseError AuthenticateResponseError } AuthenticateResponseOk ::= SEQUENCE { euiccSigned1 EuiccSigned1, -- Signed information euiccSignature1 [APPLICATION 55] OCTET STRING, --EUICC_Sign1, tag 5F37 euiccCertificate Certificate, -- eUICC Certificate (CERT.EUICC.ECDSA) signed by the EUM eumCertificate Certificate -- EUM Certificate (CERT.EUM.ECDSA) signed by the requested CI } EuiccSigned1 ::= SEQUENCE { transactionId [0] TransactionId, serverAddress [3] UTF8String, serverChallenge [4] Octet16, -- The RSP Server Challenge euiccInfo2 [34] EUICCInfo2, ctxParams1 CtxParams1 } AuthenticateResponseError ::= SEQUENCE { transactionId [0] TransactionId, authenticateErrorCode AuthenticateErrorCode } AuthenticateErrorCode ::= INTEGER {invalidCertificate(1), invalidSignature(2), unsupportedCurve(3), noSessionContext(4), invalidOid(5), euiccChallengeMismatch(6), ciPKUnknown(7), undefinedError(127)} -- Definition of Cancel Session------------------------------ CancelSessionRequest ::= [65] SEQUENCE { -- Tag 'BF41' transactionId TransactionId, -- The TransactionID generated by the RSP Server reason CancelSessionReason } CancelSessionReason ::= INTEGER {endUserRejection(0), postponed(1), timeout(2), pprNotAllowed(3), metadataMismatch(4), loadBppExecutionError(5), undefinedReason(127)} CancelSessionResponse ::= [65] CHOICE { -- Tag 'BF41' cancelSessionResponseOk CancelSessionResponseOk, cancelSessionResponseError INTEGER {invalidTransactionId(5), undefinedError(127)} } CancelSessionResponseOk ::= SEQUENCE { euiccCancelSessionSigned EuiccCancelSessionSigned, -- Signed information euiccCancelSessionSignature [APPLICATION 55] OCTET STRING -- tag '5F37 } EuiccCancelSessionSigned ::= SEQUENCE { transactionId TransactionId, smdpOid OBJECT IDENTIFIER, -- SM-DP+ OID as contained in CERT.DPauth.ECDSA reason CancelSessionReason } -- Definition of Bound Profile Package -------------------------- BoundProfilePackage ::= [54] SEQUENCE { -- Tag 'BF36' initialiseSecureChannelRequest [35] InitialiseSecureChannelRequest, -- Tag 'BF23' firstSequenceOf87 [0] SEQUENCE OF [7] OCTET STRING, -- sequence of '87' TLVs sequenceOf88 [1] SEQUENCE OF [8] OCTET STRING, -- sequence of '88' TLVs secondSequenceOf87 [2] SEQUENCE OF [7] OCTET STRING OPTIONAL, -- sequence of '87' TLVs sequenceOf86 [3] SEQUENCE OF [6] OCTET STRING -- sequence of '86' TLVs } -- Definition of Get eUICC Challenge -------------------------- GetEuiccChallengeRequest ::= [46] SEQUENCE { -- Tag 'BF2E' } GetEuiccChallengeResponse ::= [46] SEQUENCE { -- Tag 'BF2E' euiccChallenge Octet16 -- random eUICC challenge } -- Definition of Profile Installation Result ProfileInstallationResult ::= [55] SEQUENCE { -- Tag 'BF37' profileInstallationResultData [39] ProfileInstallationResultData, euiccSignPIR EuiccSignPIR } ProfileInstallationResultData ::= [39] SEQUENCE { -- Tag 'BF27' transactionId[0] TransactionId, -- The TransactionID generated by the SM-DP+ notificationMetadata[47] NotificationMetadata, smdpOid OBJECT IDENTIFIER, -- SM-DP+ OID (same value as in CERT.DPpb.ECDSA) finalResult [2] CHOICE { successResult SuccessResult, errorResult ErrorResult } } EuiccSignPIR ::= [APPLICATION 55] OCTET STRING -- Tag '5F37', eUICC’s signature SuccessResult ::= SEQUENCE { aid [APPLICATION 15] OCTET STRING (SIZE (5..16)), -- AID of ISD-P simaResponse OCTET STRING -- contains (multiple) 'EUICCResponse' as defined in [5] } ErrorResult ::= SEQUENCE { bppCommandId BppCommandId, errorReason ErrorReason, simaResponse OCTET STRING OPTIONAL -- contains (multiple) 'EUICCResponse' as defined in [5] } BppCommandId ::= INTEGER {initialiseSecureChannel(0), configureISDP(1), storeMetadata(2), storeMetadata2(3), replaceSessionKeys(4), loadProfileElements(5)} ErrorReason ::= INTEGER { incorrectInputValues(1), invalidSignature(2), invalidTransactionId(3), unsupportedCrtValues(4), unsupportedRemoteOperationType(5), unsupportedProfileClass(6), scp03tStructureError(7), scp03tSecurityError(8), installFailedDueToIccidAlreadyExistsOnEuicc(9), installFailedDueToInsufficientMemoryForProfile(10), installFailedDueToInterruption(11), installFailedDueToPEProcessingError (12), installFailedDueToDataMismatch(13), testProfileInstallFailedDueToInvalidNaaKey(14), pprNotAllowed(15), installFailedDueToUnknownError(127) } ListNotificationRequest ::= [40] SEQUENCE { -- Tag 'BF28' profileManagementOperation [1] NotificationEvent OPTIONAL } ListNotificationResponse ::= [40] CHOICE { -- Tag 'BF28' notificationMetadataList SEQUENCE OF NotificationMetadata, listNotificationsResultError INTEGER {undefinedError(127)} } NotificationMetadata ::= [47] SEQUENCE { -- Tag 'BF2F' seqNumber [0] INTEGER, profileManagementOperation [1] NotificationEvent, --Only one bit SHALL be set to 1 notificationAddress UTF8String, -- FQDN to forward the notification iccid Iccid OPTIONAL } -- Definition of Profile Nickname Information SetNicknameRequest ::= [41] SEQUENCE { -- Tag 'BF29' iccid Iccid, profileNickname [16] UTF8String (SIZE(0..64)) } SetNicknameResponse ::= [41] SEQUENCE { -- Tag 'BF29' setNicknameResult INTEGER {ok(0), iccidNotFound (1), undefinedError(127)} } id-rsp-cert-objects OBJECT IDENTIFIER ::= { id-rsp cert-objects(2)} id-rspExt OBJECT IDENTIFIER ::= {id-rsp-cert-objects 0} id-rspRole OBJECT IDENTIFIER ::= {id-rsp-cert-objects 1} -- Definition of OIDs for role identification id-rspRole-ci OBJECT IDENTIFIER ::= {id-rspRole 0} id-rspRole-euicc OBJECT IDENTIFIER ::= {id-rspRole 1} id-rspRole-eum OBJECT IDENTIFIER ::= {id-rspRole 2} id-rspRole-dp-tls OBJECT IDENTIFIER ::= {id-rspRole 3} id-rspRole-dp-auth OBJECT IDENTIFIER ::= {id-rspRole 4} id-rspRole-dp-pb OBJECT IDENTIFIER ::= {id-rspRole 5} id-rspRole-ds-tls OBJECT IDENTIFIER ::= {id-rspRole 6} id-rspRole-ds-auth OBJECT IDENTIFIER ::= {id-rspRole 7} --Definition of data objects for InitialiseSecureChannel Request InitialiseSecureChannelRequest ::= [35] SEQUENCE { -- Tag 'BF23' remoteOpId RemoteOpId, -- Remote Operation Type Identifier (value SHALL be set to installBoundProfilePackage) transactionId [0] TransactionId, -- The TransactionID generated by the SM-DP+ controlRefTemplate[6] IMPLICIT ControlRefTemplate, -- Control Reference Template (Key Agreement). Current specification considers a subset of CRT specified in GlobalPlatform Card Specification [8], section 6.4.2.3 for the Mutual Authentication Data Field smdpOtpk [APPLICATION 73] OCTET STRING, ---otPK.DP.ECKA as specified in GlobalPlatform Card Specification [8] section 6.4.2.3 for ePK.OCE.ECKA, tag '5F49' smdpSign [APPLICATION 55] OCTET STRING -- SM-DP's signature, tag '5F37' } ControlRefTemplate ::= SEQUENCE { keyType[0] Octet1, -- Key type according to GlobalPlatform Card Specification [8] Table 11-16, AES= '88', Tag '80' keyLen[1] Octet1, --Key length in number of bytes. For current specification key length SHALL by 0x10 bytes, Tag '81' hostId[4] OctetTo16 -- Host ID value , Tag '84' } --Definition of data objects for ConfigureISDPRequest ConfigureISDPRequest ::= [36] SEQUENCE { -- Tag 'BF24' dpProprietaryData [24] DpProprietaryData OPTIONAL -- Tag 'B8' } DpProprietaryData ::= SEQUENCE { -- maximum size including tag and length field: 128 bytes dpOid OBJECT IDENTIFIER -- OID in the tree of the SM-DP+ that created the Profile -- additional data objects defined by the SM-DP+ MAY follow } -- Definition of request message for command ReplaceSessionKeys ReplaceSessionKeysRequest ::= [38] SEQUENCE { -- tag 'BF26' /*The new initial MAC chaining value*/ initialMacChainingValue OCTET STRING, /*New session key value for encryption/decryption (PPK-ENC)*/ ppkEnc OCTET STRING, /*New session key value of the session key C-MAC computation/verification (PPK-MAC)*/ ppkCmac OCTET STRING } -- Definition of data objects for RetrieveNotificationsList RetrieveNotificationsListRequest ::= [43] SEQUENCE { -- Tag 'BF2B' searchCriteria CHOICE { seqNumber [0] INTEGER, profileManagementOperation [1] NotificationEvent } OPTIONAL } RetrieveNotificationsListResponse ::= [43] CHOICE { -- Tag 'BF2B' notificationList SEQUENCE OF PendingNotification, notificationsListResultError INTEGER {noResultAvailable(1), undefinedError(127)} } PendingNotification ::= CHOICE { profileInstallationResult [55] ProfileInstallationResult, -- tag 'BF37' otherSignedNotification OtherSignedNotification } OtherSignedNotification ::= SEQUENCE { tbsOtherNotification NotificationMetadata, euiccNotificationSignature [APPLICATION 55] OCTET STRING, -- eUICC signature of tbsOtherNotification, Tag '5F37' euiccCertificate Certificate, -- eUICC Certificate (CERT.EUICC.ECDSA) signed by the EUM eumCertificate Certificate -- EUM Certificate (CERT.EUM.ECDSA) signed by the requested CI } -- Definition of notificationSent NotificationSentRequest ::= [48] SEQUENCE { -- Tag 'BF30' seqNumber [0] INTEGER } NotificationSentResponse ::= [48] SEQUENCE { -- Tag 'BF30' deleteNotificationStatus INTEGER {ok(0), nothingToDelete(1), undefinedError(127)} } -- Definition of Enable Profile -------------------------- EnableProfileRequest ::= [49] SEQUENCE { -- Tag 'BF31' profileIdentifier CHOICE { isdpAid [APPLICATION 15] OctetTo16, -- AID, tag '4F' iccid Iccid -- ICCID, tag '5A' }, refreshFlag BOOLEAN -- indicating whether REFRESH is required } EnableProfileResponse ::= [49] SEQUENCE { -- Tag 'BF31' enableResult INTEGER {ok(0), iccidOrAidNotFound (1), profileNotInDisabledState(2), disallowedByPolicy(3), wrongProfileReenabling(4), catBusy(5), undefinedError(127)} } -- Definition of Disable Profile -------------------------- DisableProfileRequest ::= [50] SEQUENCE { -- Tag 'BF32' profileIdentifier CHOICE { isdpAid [APPLICATION 15] OctetTo16, -- AID, tag '4F' iccid Iccid -- ICCID, tag '5A' }, refreshFlag BOOLEAN -- indicating whether REFRESH is required } DisableProfileResponse ::= [50] SEQUENCE { -- Tag 'BF32' disableResult INTEGER {ok(0), iccidOrAidNotFound (1), profileNotInEnabledState(2), disallowedByPolicy(3), catBusy(5), undefinedError(127)} } -- Definition of Delete Profile -------------------------- DeleteProfileRequest ::= [51] CHOICE { -- Tag 'BF33' isdpAid [APPLICATION 15] OctetTo16, -- AID, tag '4F' iccid Iccid -- ICCID, tag '5A' } DeleteProfileResponse ::= [51] SEQUENCE { -- Tag 'BF33' deleteResult INTEGER {ok(0), iccidOrAidNotFound (1), profileNotInDisabledState(2), disallowedByPolicy(3), undefinedError(127)} } -- Definition of Memory Reset -------------------------- EuiccMemoryResetRequest ::= [52] SEQUENCE { -- Tag 'BF34' resetOptions [2] BIT STRING { deleteOperationalProfiles(0), deleteFieldLoadedTestProfiles(1), resetDefaultSmdpAddress(2)} } EuiccMemoryResetResponse ::= [52] SEQUENCE { -- Tag 'BF34' resetResult INTEGER {ok(0), nothingToDelete(1), catBusy(5), undefinedError(127)} } -- Definition of Get EID -------------------------- GetEuiccDataRequest ::= [62] SEQUENCE { -- Tag 'BF3E' tagList [APPLICATION 28] Octet1 -- tag '5C', the value SHALL be set to '5A' } GetEuiccDataResponse ::= [62] SEQUENCE { -- Tag 'BF3E' eidValue [APPLICATION 26] Octet16 -- tag '5A' } -- Definition of Get Rat GetRatRequest ::= [67] SEQUENCE { -- Tag ' BF43' -- No input data } GetRatResponse ::= [67] SEQUENCE { -- Tag 'BF43' rat RulesAuthorisationTable } RulesAuthorisationTable ::= SEQUENCE OF ProfilePolicyAuthorisationRule ProfilePolicyAuthorisationRule ::= SEQUENCE { pprIds PprIds, allowedOperators SEQUENCE OF OperatorId, pprFlags BIT STRING {consentRequired(0)} } -- Definition of data structure containing the list of CRL segments SegmentedCrlList ::= SEQUENCE OF CertificateList -- Definition of data structure command for loading a CRL LoadCRLRequest ::= [53] SEQUENCE { -- Tag 'BF35' -- A CRL crl CertificateList } -- Definition of data structure response for loading a CRL LoadCRLResponse ::= [53] CHOICE { -- Tag 'BF35' loadCRLResponseOk LoadCRLResponseOk, loadCRLResponseError LoadCRLResponseError } LoadCRLResponseOk ::= SEQUENCE { missingParts SEQUENCE OF INTEGER OPTIONAL } LoadCRLResponseError ::= INTEGER {invalidSignature(1), invalidCRLFormat(2), notEnoughMemorySpace(3), verificationKeyNotFound(4), fresherCrlAlreadyLoaded(5), baseCrlMissing(6), undefinedError(127)} -- Definition of the extension for Certificate Expiration Date id-rsp-expDate OBJECT IDENTIFIER ::= {id-rspExt 1} ExpirationDate ::= Time -- Definition of the extension id for total partial-CRL number id-rsp-totalPartialCrlNumber OBJECT IDENTIFIER ::= {id-rspExt 2} TotalPartialCrlNumber ::= INTEGER -- Definition of the extension id for the partial-CRL number id-rsp-partialCrlNumber OBJECT IDENTIFIER ::= {id-rspExt 3} PartialCrlNumber ::= INTEGER -- Definition for ES9+ ASN.1 Binding -------------------------- RemoteProfileProvisioningRequest ::= [2] CHOICE { -- Tag 'A2' initiateAuthenticationRequest [57] InitiateAuthenticationRequest, -- Tag 'BF39' authenticateClientRequest [59] AuthenticateClientRequest, -- Tag 'BF3B' getBoundProfilePackageRequest [58] GetBoundProfilePackageRequest, -- Tag 'BF3A' cancelSessionRequestEs9 [65] CancelSessionRequestEs9, -- Tag 'BF41' handleNotification [61] HandleNotification -- tag 'BF3D' } RemoteProfileProvisioningResponse ::= [2] CHOICE { -- Tag 'A2' initiateAuthenticationResponse [57] InitiateAuthenticationResponse, -- Tag 'BF39' authenticateClientResponseEs9 [59] AuthenticateClientResponseEs9, -- Tag 'BF3B' getBoundProfilePackageResponse [58] GetBoundProfilePackageResponse, -- Tag 'BF3A' cancelSessionResponseEs9 [65] CancelSessionResponseEs9, -- Tag 'BF41' authenticateClientResponseEs11 [64] AuthenticateClientResponseEs11 -- Tag 'BF40' } InitiateAuthenticationRequest ::= [57] SEQUENCE { -- Tag 'BF39' euiccChallenge [1] Octet16, -- random eUICC challenge smdpAddress [3] UTF8String, euiccInfo1 EUICCInfo1 } InitiateAuthenticationResponse ::= [57] CHOICE { -- Tag 'BF39' initiateAuthenticationOk InitiateAuthenticationOkEs9, initiateAuthenticationError INTEGER { invalidDpAddress(1), euiccVersionNotSupportedByDp(2), ciPKNotSupported(3) } } InitiateAuthenticationOkEs9 ::= SEQUENCE { transactionId [0] TransactionId, -- The TransactionID generated by the SM-DP+ serverSigned1 ServerSigned1, -- Signed information serverSignature1 [APPLICATION 55] OCTET STRING, -- Server_Sign1, tag '5F37' euiccCiPKIdToBeUsed SubjectKeyIdentifier, -- The curve CI Public Key to be used as required by ES10b.AuthenticateServer serverCertificate Certificate } AuthenticateClientRequest ::= [59] SEQUENCE { -- Tag 'BF3B' transactionId [0] TransactionId, authenticateServerResponse [56] AuthenticateServerResponse -- This is the response from ES10b.AuthenticateServer } AuthenticateClientResponseEs9 ::= [59] CHOICE { -- Tag 'BF3B' authenticateClientOk AuthenticateClientOk, authenticateClientError INTEGER { eumCertificateInvalid(1), eumCertificateExpired(2), euiccCertificateInvalid(3), euiccCertificateExpired(4), euiccSignatureInvalid(5), matchingIdRefused(6), eidMismatch(7), noEligibleProfile(8), ciPKUnknown(9), invalidTransactionId(10), insufficientMemory(11), undefinedError(127) } } AuthenticateClientOk ::= SEQUENCE { transactionId [0] TransactionId, profileMetaData [37] StoreMetadataRequest, smdpSigned2 SmdpSigned2, -- Signed information smdpSignature2 [APPLICATION 55] OCTET STRING, -- tag '5F37' smdpCertificate Certificate -- CERT.DPpb.ECDSA } GetBoundProfilePackageRequest ::= [58] SEQUENCE { -- Tag 'BF3A' transactionId [0] TransactionId, prepareDownloadResponse [33] PrepareDownloadResponse } GetBoundProfilePackageResponse ::= [58] CHOICE { -- Tag 'BF3A' getBoundProfilePackageOk GetBoundProfilePackageOk, getBoundProfilePackageError INTEGER { euiccSignatureInvalid(1), confirmationCodeMissing(2), confirmationCodeRefused(3), confirmationCodeRetriesExceeded(4), bppRebindingRefused(5), downloadOrderExpired(6), invalidTransactionId(95), undefinedError(127) } } GetBoundProfilePackageOk ::= SEQUENCE { transactionId [0] TransactionId, boundProfilePackage [54] BoundProfilePackage } HandleNotification ::= [61] SEQUENCE { -- Tag 'BF3D' pendingNotification PendingNotification } CancelSessionRequestEs9 ::= [65] SEQUENCE { -- Tag 'BF41' transactionId TransactionId, cancelSessionResponse CancelSessionResponse -- data structure defined for ES10b.CancelSession function } CancelSessionResponseEs9 ::= [65] CHOICE { -- Tag 'BF41' cancelSessionOk CancelSessionOk, cancelSessionError INTEGER { invalidTransactionId(1), euiccSignatureInvalid(2), undefinedError(127) } } CancelSessionOk ::= SEQUENCE { -- This function has no output data } EuiccConfiguredAddressesRequest ::= [60] SEQUENCE { -- Tag 'BF3C' } EuiccConfiguredAddressesResponse ::= [60] SEQUENCE { -- Tag 'BF3C' defaultDpAddress UTF8String OPTIONAL, -- Default SM-DP+ address as an FQDN rootDsAddress UTF8String -- Root SM-DS address as an FQDN } ISDRProprietaryApplicationTemplate ::= [PRIVATE 0] SEQUENCE { -- Tag 'E0' svn [2] VersionType, -- GSMA SGP.22 version supported (SVN) lpaeSupport BIT STRING { lpaeUsingCat(0), -- LPA in the eUICC using Card Application Toolkit lpaeUsingScws(1) -- LPA in the eUICC using Smartcard Web Server } OPTIONAL } LpaeActivationRequest ::= [66] SEQUENCE { -- Tag 'BF42' lpaeOption BIT STRING { activateCatBasedLpae(0), -- LPAe with LUIe based on CAT activateScwsBasedLpae(1) -- LPAe with LUIe based on SCWS } } LpaeActivationResponse ::= [66] SEQUENCE { -- Tag 'BF42' lpaeActivationResult INTEGER {ok(0), notSupported(1)} } SetDefaultDpAddressRequest ::= [63] SEQUENCE { -- Tag 'BF3F' defaultDpAddress UTF8String -- Default SM-DP+ address as an FQDN } SetDefaultDpAddressResponse ::= [63] SEQUENCE { -- Tag 'BF3F' setDefaultDpAddressResult INTEGER { ok (0), undefinedError (127)} } AuthenticateClientResponseEs11 ::= [64] CHOICE { -- Tag 'BF40' authenticateClientOk AuthenticateClientOkEs11, authenticateClientError INTEGER { eumCertificateInvalid(1), eumCertificateExpired(2), euiccCertificateInvalid(3), euiccCertificateExpired(4), euiccSignatureInvalid(5), eventIdUnknown(6), invalidTransactionId(7), undefinedError(127) } } AuthenticateClientOkEs11 ::= SEQUENCE { transactionId TransactionId, eventEntries SEQUENCE OF EventEntries } EventEntries ::= SEQUENCE { eventId UTF8String, rspServerAddress UTF8String } ENDestkme-group-lpac-c2fcf5e/driver/000077500000000000000000000000001504765665400171455ustar00rootroot00000000000000estkme-group-lpac-c2fcf5e/driver/CMakeLists.txt000066400000000000000000000150321504765665400217060ustar00rootroot00000000000000include(CMakeDependentOption) cmake_dependent_option(LPAC_DYNAMIC_DRIVERS "Build lpac/libeuicc driver backends as a dynamic library" OFF "LPAC_DYNAMIC_LIBEUICC" OFF) option(LPAC_WITH_APDU_PCSC "Build APDU PCSC Backend (requires PCSC libraries)" ON) cmake_dependent_option(LPAC_WITH_APDU_AT "Build APDU AT Backend" ON UNIX OFF) cmake_dependent_option(LPAC_WITH_APDU_AT_WIN32 "Build APDU AT Backend for Win32" ON WIN32 OFF) option(LPAC_WITH_APDU_GBINDER "Build APDU Gbinder backend for libhybris devices (requires gbinder headers)" OFF) option(LPAC_WITH_APDU_QMI "Build QMI backend for Qualcomm devices (requires libqmi)" OFF) option(LPAC_WITH_APDU_QMI_QRTR "Build QMI-over-QRTR backend for Qualcomm devices (requires libqrtr and libqmi headers)" OFF) option(LPAC_WITH_APDU_MBIM "Build MBIM backend for MBIM devices (requires libmbim)" OFF) option(LPAC_WITH_HTTP_CURL "Build HTTP Curl interface" ON) aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} DIR_INTERFACE_SRCS) if(LPAC_DYNAMIC_DRIVERS) add_library(euicc-drivers SHARED ${DIR_INTERFACE_SRCS}) list(APPEND LIBEUICC_DRIVERS_REQUIRES "libeuicc = ${PROJECT_VERSION}") else() add_library(euicc-drivers STATIC ${DIR_INTERFACE_SRCS}) endif() target_link_libraries(euicc-drivers euicc cjson-static lpac-utils) target_include_directories(euicc-drivers PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_sources(euicc-drivers PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/apdu/stdio.c) target_sources(euicc-drivers PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/http/stdio.c) if(LPAC_WITH_APDU_PCSC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DLPAC_WITH_APDU_PCSC") target_sources(euicc-drivers PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/apdu/pcsc.c) if(WIN32) target_sources(euicc-drivers PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/apdu/pcsc_win32.c) target_link_libraries(euicc-drivers winscard) elseif(APPLE) target_link_libraries(euicc-drivers "-framework PCSC") if(LPAC_DYNAMIC_DRIVERS) # for pkg-config set(LIBEUICC_DRIVERS_EXTRA_CFLAGS "-framework PCSC") endif() else() find_package(PCSCLite) target_link_libraries(euicc-drivers PCSCLite::PCSCLite) if(LPAC_DYNAMIC_DRIVERS) list(APPEND LIBEUICC_DRIVERS_REQUIRES "libpcsclite") endif() endif() endif() if(LPAC_WITH_APDU_AT) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DLPAC_WITH_APDU_AT") target_sources(euicc-drivers PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/apdu/at_common.c ${CMAKE_CURRENT_SOURCE_DIR}/apdu/at.c ) endif() if(LPAC_WITH_APDU_AT_WIN32) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DLPAC_WITH_APDU_AT_WIN32") target_link_libraries(euicc-drivers setupapi) target_sources(euicc-drivers PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/apdu/at_common.c ${CMAKE_CURRENT_SOURCE_DIR}/apdu/at_win32.c ) endif() if(LPAC_WITH_APDU_GBINDER) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DLPAC_WITH_APDU_GBINDER") target_sources(euicc-drivers PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/apdu/gbinder_hidl.c) find_package(PkgConfig REQUIRED) pkg_check_modules(GBINDER REQUIRED IMPORTED_TARGET libgbinder) pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) target_link_libraries(euicc-drivers PkgConfig::GBINDER PkgConfig::GLIB) if(LPAC_DYNAMIC_DRIVERS) list(APPEND LIBEUICC_DRIVERS_REQUIRES "libgbinder") list(APPEND LIBEUICC_DRIVERS_REQUIRES "glib-2.0") endif() endif() if(LPAC_WITH_APDU_QMI) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DLPAC_WITH_APDU_QMI") target_sources(euicc-drivers PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/apdu/qmi.c ${CMAKE_CURRENT_SOURCE_DIR}/apdu/qmi_helpers.c ${CMAKE_CURRENT_SOURCE_DIR}/apdu/qmi_common.c) find_package(PkgConfig REQUIRED) pkg_check_modules(QMI_GLIB REQUIRED IMPORTED_TARGET qmi-glib>=1.35.5) target_link_libraries(euicc-drivers PkgConfig::QMI_GLIB) if(LPAC_DYNAMIC_DRIVERS) list(APPEND LIBEUICC_DRIVERS_REQUIRES "qmi-glib") endif() endif() if(LPAC_WITH_APDU_QMI_QRTR) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DLPAC_WITH_APDU_QMI_QRTR") target_sources(euicc-drivers PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/apdu/qmi_qrtr.c ${CMAKE_CURRENT_SOURCE_DIR}/apdu/qmi_helpers.c ${CMAKE_CURRENT_SOURCE_DIR}/apdu/qmi_common.c) find_package(PkgConfig REQUIRED) pkg_check_modules(QRTR_GLIB REQUIRED IMPORTED_TARGET qrtr-glib) pkg_check_modules(QMI_GLIB REQUIRED IMPORTED_TARGET qmi-glib>=1.35.5) target_link_libraries(euicc-drivers PkgConfig::QRTR_GLIB PkgConfig::QMI_GLIB) if(LPAC_DYNAMIC_DRIVERS) list(APPEND LIBEUICC_DRIVERS_REQUIRES "qrtr-glib") list(APPEND LIBEUICC_DRIVERS_REQUIRES "qmi-glib") endif() endif() if(LPAC_WITH_APDU_MBIM) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DLPAC_WITH_APDU_MBIM") target_sources(euicc-drivers PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/apdu/mbim.c ${CMAKE_CURRENT_SOURCE_DIR}/apdu/mbim_helpers.c) find_package(PkgConfig REQUIRED) pkg_check_modules(MBIM_GLIB REQUIRED IMPORTED_TARGET mbim-glib) target_link_libraries(euicc-drivers PkgConfig::MBIM_GLIB PkgConfig::MBIM_GLIB) if(LPAC_DYNAMIC_DRIVERS) list(APPEND LIBEUICC_DRIVERS_REQUIRES "mbim-glib") endif() endif() if(LPAC_WITH_HTTP_CURL) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DLPAC_WITH_HTTP_CURL") target_sources(euicc-drivers PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/http/curl.c) if(WIN32) target_link_libraries(euicc-drivers ${DL_LIBRARY}) else() find_package(CURL REQUIRED) target_link_libraries(euicc-drivers curl) if(LPAC_DYNAMIC_DRIVERS) list(APPEND LIBEUICC_DRIVERS_REQUIRES "libcurl") endif() endif() endif() if(LPAC_DYNAMIC_DRIVERS) # Install headers file(GLOB ALL_HEADERS "*.h") foreach(header ${ALL_HEADERS}) if(${header} MATCHES "^.*\.private\.h$") list(REMOVE_ITEM ALL_HEADERS ${header}) endif() endforeach() set_target_properties(euicc-drivers PROPERTIES PUBLIC_HEADER "${ALL_HEADERS}") # Install a pkg-config file (mainly for Linux; macOS is untested; Win32 is not supported) if(UNIX) list(JOIN LIBEUICC_DRIVERS_REQUIRES ", " LIBEUICC_DRIVERS_REQUIRES) configure_file(libeuicc-drivers.pc.in libeuicc-drivers.pc @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libeuicc-drivers.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) endif() set_target_properties(euicc-drivers PROPERTIES SOVERSION ${PROJECT_VERSION_MAJOR}) install(TARGETS euicc-drivers LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/euicc) endif() estkme-group-lpac-c2fcf5e/driver/LICENSE000066400000000000000000001033331504765665400201550ustar00rootroot00000000000000 GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software. A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public. The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version. An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU Affero General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Remote Network Interaction; Use with the GNU General Public License. Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU Affero General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements. You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see . estkme-group-lpac-c2fcf5e/driver/apdu/000077500000000000000000000000001504765665400200765ustar00rootroot00000000000000estkme-group-lpac-c2fcf5e/driver/apdu/at.c000066400000000000000000000144231504765665400206520ustar00rootroot00000000000000#include "at.h" #include "at_common.h" #include #include #include #include #include #include #include #include #include static FILE *fuart; static int logic_channel = 0; static char *buffer; static void enumerate_serial_device_linux(cJSON *data) { const char *dir_path = "/dev/serial/by-id"; DIR *dir = opendir(dir_path); if (dir == NULL) return; struct dirent *entry; while ((entry = readdir(dir)) != NULL) { if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } size_t path_len = strlen(dir_path) + 1 /* SEP */ + strlen(entry->d_name) + 1 /* NUL */; _cleanup_free_ char *full_path = malloc(path_len); snprintf(full_path, path_len, "%s/%s", dir_path, entry->d_name); cJSON *item = cJSON_CreateObject(); cJSON_AddStringToObject(item, "env", full_path); cJSON_AddStringToObject(item, "name", entry->d_name); cJSON_AddItemToArray(data, item); } closedir(dir); } static int at_expect(char **response, const char *expected) { memset(buffer, 0, AT_BUFFER_SIZE); if (response) *response = NULL; while (1) { fgets(buffer, AT_BUFFER_SIZE, fuart); buffer[strcspn(buffer, "\r\n")] = 0; if (getenv_or_default(ENV_AT_DEBUG, false)) printf("AT_DEBUG: %s\n", buffer); if (strcmp(buffer, "ERROR") == 0) { return -1; } else if (strcmp(buffer, "OK") == 0) { return 0; } else if (expected && strncmp(buffer, expected, strlen(expected)) == 0) { if (response) *response = strdup(buffer + strlen(expected)); } } return 0; } static int apdu_interface_connect(struct euicc_ctx *ctx) { const char *device = getenv_or_default(ENV_AT_DEVICE, "/dev/ttyUSB0"); logic_channel = 0; fuart = fopen(device, "r+"); if (fuart == NULL) { fprintf(stderr, "Failed to open device: %s\n", device); return -1; } setbuf(fuart, NULL); fprintf(fuart, "AT+CCHO=?\r\n"); if (at_expect(NULL, NULL)) { fprintf(stderr, "Device missing AT+CCHO support\n"); return -1; } fprintf(fuart, "AT+CCHC=?\r\n"); if (at_expect(NULL, NULL)) { fprintf(stderr, "Device missing AT+CCHC support\n"); return -1; } fprintf(fuart, "AT+CGLA=?\r\n"); if (at_expect(NULL, NULL)) { fprintf(stderr, "Device missing AT+CGLA support\n"); return -1; } return 0; } static void apdu_interface_disconnect(struct euicc_ctx *ctx) { fclose(fuart); fuart = NULL; logic_channel = 0; } static int apdu_interface_transmit(struct euicc_ctx *ctx, uint8_t **rx, uint32_t *rx_len, const uint8_t *tx, uint32_t tx_len) { int fret = 0; int ret; _cleanup_free_ char *response = NULL; char *hexstr = NULL; *rx = NULL; *rx_len = 0; if (!logic_channel) { return -1; } fprintf(fuart, "AT+CGLA=%d,%u,\"", logic_channel, tx_len * 2); for (uint32_t i = 0; i < tx_len; i++) { fprintf(fuart, "%02X", (uint8_t)(tx[i] & 0xFF)); } fprintf(fuart, "\"\r\n"); if (at_expect(&response, "+CGLA:")) { goto err; } if (response == NULL) { goto err; } strtok(response, ","); hexstr = strtok(NULL, ","); if (!hexstr) { goto err; } if (hexstr[0] == '"') { hexstr++; } hexstr[strcspn(hexstr, "\"")] = '\0'; *rx_len = strlen(hexstr) / 2; *rx = malloc(*rx_len); if (!*rx) { goto err; } ret = euicc_hexutil_hex2bin_r(*rx, *rx_len, hexstr, strlen(hexstr)); if (ret < 0) { goto err; } *rx_len = ret; goto exit; err: fret = -1; free(*rx); *rx = NULL; *rx_len = 0; exit: return fret; } static int apdu_interface_logic_channel_open(struct euicc_ctx *ctx, const uint8_t *aid, uint8_t aid_len) { char *response; if (logic_channel) { return logic_channel; } for (int i = 1; i <= 4; i++) { fprintf(fuart, "AT+CCHC=%d\r\n", i); at_expect(NULL, NULL); } fprintf(fuart, "AT+CCHO=\""); for (int i = 0; i < aid_len; i++) { fprintf(fuart, "%02X", (uint8_t)(aid[i] & 0xFF)); } fprintf(fuart, "\"\r\n"); if (at_expect(&response, "+CCHO: ")) { return -1; } if (response == NULL) { return -1; } logic_channel = atoi(response); return logic_channel; } static void apdu_interface_logic_channel_close(struct euicc_ctx *ctx, uint8_t channel) { if (!logic_channel) { return; } fprintf(fuart, "AT+CCHC=%d\r\n", logic_channel); at_expect(NULL, NULL); } static int libapduinterface_init(struct euicc_apdu_interface *ifstruct) { set_deprecated_env_name(ENV_AT_DEBUG, "AT_DEBUG"); set_deprecated_env_name(ENV_AT_DEVICE, "AT_DEVICE"); memset(ifstruct, 0, sizeof(struct euicc_apdu_interface)); ifstruct->connect = apdu_interface_connect; ifstruct->disconnect = apdu_interface_disconnect; ifstruct->logic_channel_open = apdu_interface_logic_channel_open; ifstruct->logic_channel_close = apdu_interface_logic_channel_close; ifstruct->transmit = apdu_interface_transmit; buffer = malloc(AT_BUFFER_SIZE); if (!buffer) { fprintf(stderr, "Failed to allocate memory\n"); return -1; } return 0; } static int libapduinterface_main(const int argc, char **argv) { if (argc < 2) { fprintf(stderr, "Usage: %s \n", argv[0]); return -1; } if (strcmp(argv[1], "list") == 0) { _cleanup_cjson_ cJSON *data = cJSON_CreateArray(); #ifdef __linux__ enumerate_serial_device_linux(data); #else fprintf(stderr, "Serial device enumeration not implemented on this platform.\n"); fflush(stderr); #endif jprint_enumerate_devices(data); } return 0; } static void libapduinterface_fini(struct euicc_apdu_interface *ifstruct) { free(buffer); } const struct euicc_driver driver_apdu_at = { .type = DRIVER_APDU, .name = "at", .init = (int (*)(void *))libapduinterface_init, .main = libapduinterface_main, .fini = (void (*)(void *))libapduinterface_fini, }; estkme-group-lpac-c2fcf5e/driver/apdu/at.h000066400000000000000000000001341504765665400206510ustar00rootroot00000000000000#pragma once #include extern const struct euicc_driver driver_apdu_at; estkme-group-lpac-c2fcf5e/driver/apdu/at_common.c000066400000000000000000000004551504765665400222220ustar00rootroot00000000000000#include "at_common.h" #include bool jprint_enumerate_devices(cJSON *data) { cJSON *payload = cJSON_CreateObject(); cJSON_AddStringOrNullToObject(payload, "env", ENV_AT_DEVICE); cJSON_AddItemToObject(payload, "data", data); return json_print("driver", payload); } estkme-group-lpac-c2fcf5e/driver/apdu/at_common.h000066400000000000000000000003611504765665400222230ustar00rootroot00000000000000#pragma once #include #define AT_BUFFER_SIZE 20480 #define AT_READ_BUFFER_SIZE 4096 #define ENV_AT_DEBUG APDU_ENV_NAME(AT, DEBUG) #define ENV_AT_DEVICE APDU_ENV_NAME(AT, DEVICE) bool jprint_enumerate_devices(cJSON *data); estkme-group-lpac-c2fcf5e/driver/apdu/at_win32.c000066400000000000000000000254111504765665400216730ustar00rootroot00000000000000#include "at_win32.h" #include "at_common.h" #include #include #include #include #include // windows.h MUST before other Windows headers #include #include #include #include #include #include #include #include #pragma comment(lib, "setupapi.lib") static HANDLE hComm; static int logic_channel = 0; static char *at_cmd_buffer; static char at_read_buffer[AT_READ_BUFFER_SIZE]; static DWORD at_read_buffer_len = 0; int starts_with(const char *str, const char *prefix) { size_t len_prefix = strlen(prefix); return strncmp(str, prefix, len_prefix) == 0; } static void enumerate_com_ports(cJSON *data) { HDEVINFO hDevInfo; SP_DEVINFO_DATA devInfoData; DWORD i; hDevInfo = SetupDiGetClassDevsW(&GUID_DEVCLASS_PORTS, 0, 0, DIGCF_PRESENT); if (hDevInfo == INVALID_HANDLE_VALUE) return; devInfoData.cbSize = sizeof(SP_DEVINFO_DATA); for (i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &devInfoData); i++) { wchar_t portName[256] = {0}; wchar_t friendlyName[256] = {0}; char portNameMB[256] = {0}; char friendlyNameMB[256] = {0}; DWORD size = sizeof(portName); HKEY hKey = SetupDiOpenDevRegKey(hDevInfo, &devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); if (hKey != INVALID_HANDLE_VALUE) { DWORD type; if (RegQueryValueExW(hKey, L"PortName", NULL, &type, (LPBYTE)portName, &size) != ERROR_SUCCESS || type != REG_SZ) { portName[0] = L'\0'; } } if (!SetupDiGetDeviceRegistryPropertyW(hDevInfo, &devInfoData, SPDRP_FRIENDLYNAME, NULL, (PBYTE)friendlyName, sizeof(friendlyName), NULL)) { friendlyName[0] = L'\0'; } WideCharToMultiByte(CP_ACP, 0, portName, -1, portNameMB, sizeof(portNameMB), NULL, NULL); WideCharToMultiByte(CP_ACP, 0, friendlyName, -1, friendlyNameMB, sizeof(friendlyNameMB), NULL, NULL); if (starts_with(portNameMB, "COM")) { cJSON *item = cJSON_CreateObject(); if (item) { cJSON_AddStringToObject(item, "env", portNameMB); cJSON_AddStringToObject(item, "name", friendlyNameMB[0] ? friendlyNameMB : portNameMB); cJSON_AddItemToArray(data, item); } else { cJSON_Delete(item); } } RegCloseKey(hKey); } SetupDiDestroyDeviceInfoList(hDevInfo); } static int at_expect(char **response, const char *expected) { char line[AT_BUFFER_SIZE]; DWORD bytes_read; char *found_response_data = NULL; int result = -1; if (response) *response = NULL; while (1) { char *newline = memchr(at_read_buffer, '\n', at_read_buffer_len); if (!newline) { if (at_read_buffer_len >= sizeof(at_read_buffer)) { fprintf(stderr, "AT response line too long or buffer full\n"); at_read_buffer_len = 0; return -1; } if (!ReadFile(hComm, at_read_buffer + at_read_buffer_len, sizeof(at_read_buffer) - at_read_buffer_len, &bytes_read, NULL)) { fprintf(stderr, "ReadFile error: %lu\n", GetLastError()); return -1; } if (bytes_read == 0) { fprintf(stderr, "AT command timeout\n"); return -1; } at_read_buffer_len += bytes_read; continue; } int line_len = (newline - at_read_buffer); memcpy(line, at_read_buffer, line_len); line[line_len] = '\0'; memmove(at_read_buffer, newline + 1, at_read_buffer_len - line_len - 1); at_read_buffer_len -= (line_len + 1); line[strcspn(line, "\r")] = 0; if (strlen(line) == 0) { continue; } if (getenv_or_default(ENV_AT_DEBUG, false)) fprintf(stderr, "AT_DEBUG_RX: %s\n", line); if (strcmp(line, "ERROR") == 0) { result = -1; goto end; } else if (strcmp(line, "OK") == 0) { result = 0; goto end; } else if (expected && strncmp(line, expected, strlen(expected)) == 0) { free(found_response_data); found_response_data = strdup(line + strlen(expected)); } } end: if (result == 0) { if (response) { *response = found_response_data; } else { free(found_response_data); } } else { free(found_response_data); } return result; } static int at_write_command(const char *cmd) { DWORD bytes_written; if (getenv_or_default(ENV_AT_DEBUG, false)) fprintf(stderr, "AT_DEBUG_TX: %s", cmd); if (!WriteFile(hComm, cmd, strlen(cmd), &bytes_written, NULL)) { fprintf(stderr, "Failed to write to port, error: %lu\n", GetLastError()); return -1; } return 0; } static int apdu_interface_connect(struct euicc_ctx *ctx) { const char *device = getenv_or_default(ENV_AT_DEVICE, "COM3"); DCB dcb = {0}; logic_channel = 0; char dev_ascii[64]; snprintf(dev_ascii, sizeof(dev_ascii), "\\\\.\\%s", device); wchar_t devname[64]; mbstowcs(devname, dev_ascii, sizeof(devname) / sizeof(wchar_t)); hComm = CreateFileW(devname, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hComm == INVALID_HANDLE_VALUE) { fprintf(stderr, "Failed to open device: %s, error: %lu\n", dev_ascii, GetLastError()); return -1; } PurgeComm(hComm, PURGE_TXCLEAR | PURGE_RXCLEAR); dcb.DCBlength = sizeof(dcb); if (!GetCommState(hComm, &dcb)) { fprintf(stderr, "GetCommState failed, error: %lu\n", GetLastError()); CloseHandle(hComm); return -1; } if (at_write_command("AT\r\n") || at_expect(NULL, NULL)) { fprintf(stderr, "Device missing AT command support\n"); CloseHandle(hComm); return -1; } if (at_write_command("AT+CCHO=?\r\n") || at_expect(NULL, NULL)) { fprintf(stderr, "Device missing AT+CCHO support\n"); CloseHandle(hComm); return -1; } if (at_write_command("AT+CCHC=?\r\n") || at_expect(NULL, NULL)) { fprintf(stderr, "Device missing AT+CCHC support\n"); CloseHandle(hComm); return -1; } if (at_write_command("AT+CGLA=?\r\n") || at_expect(NULL, NULL)) { fprintf(stderr, "Device missing AT+CGLA support\n"); CloseHandle(hComm); return -1; } return 0; } static void apdu_interface_disconnect(struct euicc_ctx *ctx) { if (hComm != INVALID_HANDLE_VALUE) { CloseHandle(hComm); hComm = INVALID_HANDLE_VALUE; } logic_channel = 0; } static int apdu_interface_transmit(struct euicc_ctx *ctx, uint8_t **rx, uint32_t *rx_len, const uint8_t *tx, uint32_t tx_len) { int fret = 0; int ret; _cleanup_free_ char *response = NULL; char *hexstr = NULL; *rx = NULL; *rx_len = 0; if (!logic_channel) { return -1; } size_t cmd_len = snprintf(at_cmd_buffer, AT_BUFFER_SIZE, "AT+CGLA=%d,%u,\"", logic_channel, tx_len * 2); char *p = at_cmd_buffer + cmd_len; for (uint32_t i = 0; i < tx_len; i++) { sprintf(p, "%02X", tx[i]); p += 2; } sprintf(p, "\"\r\n"); if (at_write_command(at_cmd_buffer) || at_expect(&response, "+CGLA: ")) { goto err; } if (response == NULL) { goto err; } strtok(response, ","); hexstr = strtok(NULL, ","); if (!hexstr) { goto err; } if (hexstr[0] == '"') { hexstr++; } hexstr[strcspn(hexstr, "\"")] = '\0'; *rx_len = strlen(hexstr) / 2; *rx = malloc(*rx_len); if (!*rx) { goto err; } ret = euicc_hexutil_hex2bin_r(*rx, *rx_len, hexstr, strlen(hexstr)); if (ret < 0) { goto err; } *rx_len = ret; goto exit; err: fret = -1; free(*rx); *rx = NULL; *rx_len = 0; exit: return fret; } static int apdu_interface_logic_channel_open(struct euicc_ctx *ctx, const uint8_t *aid, uint8_t aid_len) { _cleanup_free_ char *response = NULL; if (logic_channel) { return logic_channel; } for (int i = 1; i <= 4; i++) { snprintf(at_cmd_buffer, AT_BUFFER_SIZE, "AT+CCHC=%d\r\n", i); at_write_command(at_cmd_buffer); at_expect(NULL, NULL); } size_t cmd_len = snprintf(at_cmd_buffer, AT_BUFFER_SIZE, "AT+CCHO=\""); char *p = at_cmd_buffer + cmd_len; for (int i = 0; i < aid_len; i++) { sprintf(p, "%02X", aid[i]); p += 2; } sprintf(p, "\"\r\n"); if (at_write_command(at_cmd_buffer) || at_expect(&response, "+CCHO: ")) { return -1; } if (response == NULL) { return -1; } logic_channel = atoi(response); return logic_channel; } static void apdu_interface_logic_channel_close(struct euicc_ctx *ctx, uint8_t channel) { if (!logic_channel) { return; } snprintf(at_cmd_buffer, AT_BUFFER_SIZE, "AT+CCHC=%d\r\n", logic_channel); at_write_command(at_cmd_buffer); at_expect(NULL, NULL); logic_channel = 0; } static int libapduinterface_init(struct euicc_apdu_interface *ifstruct) { set_deprecated_env_name(ENV_AT_DEBUG, "AT_DEBUG"); set_deprecated_env_name(ENV_AT_DEVICE, "AT_DEVICE"); memset(ifstruct, 0, sizeof(struct euicc_apdu_interface)); ifstruct->connect = apdu_interface_connect; ifstruct->disconnect = apdu_interface_disconnect; ifstruct->logic_channel_open = apdu_interface_logic_channel_open; ifstruct->logic_channel_close = apdu_interface_logic_channel_close; ifstruct->transmit = apdu_interface_transmit; at_cmd_buffer = malloc(AT_BUFFER_SIZE); if (!at_cmd_buffer) { fprintf(stderr, "Failed to allocate memory\n"); return -1; } hComm = INVALID_HANDLE_VALUE; return 0; } static int libapduinterface_main(int argc, char **argv) { if (argc < 2) { fprintf(stderr, "Usage: %s \n", argv[0]); return -1; } if (strcmp(argv[1], "list") == 0) { cJSON *data = cJSON_CreateArray(); enumerate_com_ports(data); jprint_enumerate_devices(data); return 0; } return 0; } static void libapduinterface_fini(struct euicc_apdu_interface *ifstruct) { free(at_cmd_buffer); if (hComm != INVALID_HANDLE_VALUE) { CloseHandle(hComm); } } const struct euicc_driver driver_apdu_at_win32 = { .type = DRIVER_APDU, .name = "at", .init = (int (*)(void *))libapduinterface_init, .main = libapduinterface_main, .fini = (void (*)(void *))libapduinterface_fini, }; estkme-group-lpac-c2fcf5e/driver/apdu/at_win32.h000066400000000000000000000001431504765665400216730ustar00rootroot00000000000000 #pragma once #include extern const struct euicc_driver driver_apdu_at_win32; estkme-group-lpac-c2fcf5e/driver/apdu/gbinder_hidl.c000066400000000000000000000247651504765665400226720ustar00rootroot00000000000000#include "gbinder_hidl.h" #include #include #include #include #include #include #include #define ENV_DEBUG APDU_ENV_NAME(GBINDER, DEBUG) #define HIDL_SERVICE_DEVICE "/dev/hwbinder" #define HIDL_SERVICE_IFACE "android.hardware.radio@1.0::IRadio" #define HIDL_SERVICE_IFACE_CALLBACK "android.hardware.radio@1.0::IRadioResponse" // ref: IRadio #define HIDL_SERVICE_SET_RESPONSE_FUNCTIONS GBINDER_FIRST_CALL_TRANSACTION #define HIDL_SERVICE_ICC_OPEN_LOGICAL_CHANNEL (GBINDER_FIRST_CALL_TRANSACTION + 105) #define HIDL_SERVICE_ICC_CLOSE_LOGICAL_CHANNEL (GBINDER_FIRST_CALL_TRANSACTION + 106) #define HIDL_SERVICE_ICC_TRANSMIT_APDU_LOGICAL_CHANNEL (GBINDER_FIRST_CALL_TRANSACTION + 107) // ref: IRadioResponse #define HIDL_SERVICE_ICC_OPEN_LOGICAL_CHANNEL_CALLBACK (GBINDER_FIRST_CALL_TRANSACTION + 104) #define HIDL_SERVICE_ICC_CLOSE_LOGICAL_CHANNEL_CALLBACK (GBINDER_FIRST_CALL_TRANSACTION + 105) #define HIDL_SERVICE_ICC_TRANSMIT_APDU_LOGICAL_CHANNEL_CALLBACK (GBINDER_FIRST_CALL_TRANSACTION + 106) static int lastChannelId = -1; struct radio_response_info { int32_t type; int32_t serial; int32_t error; }; struct icc_io_result { int32_t sw1; int32_t sw2; GBinderHidlString simResponse; }; struct sim_apdu { int32_t sessionId; int32_t cla; int32_t instruction; int32_t p1; int32_t p2; int32_t p3; GBinderHidlString data; }; static const GBinderWriterField sim_apdu_f[] = {GBINDER_WRITER_FIELD_HIDL_STRING(struct sim_apdu, data), GBINDER_WRITER_FIELD_END()}; static const GBinderWriterType sim_apdu_t = {GBINDER_WRITER_STRUCT_NAME_AND_SIZE(struct sim_apdu), sim_apdu_f}; static GBinderServiceManager *sm; // IRadioResponse static GBinderLocalObject *response_callback; // IRadio static GBinderRemoteObject *remote; static GBinderClient *client; static GMainLoop *binder_loop; static int lastIntResp = -1; static int lastRadioErr = 0; static struct icc_io_result lastIccIoResult = {0}; static GBinderLocalReply *radio_response_transact(GBinderLocalObject *obj, GBinderRemoteRequest *req, guint code, guint flags, int *status, void *user_data) { GBinderReader reader; gbinder_remote_request_init_reader(req, &reader); const struct radio_response_info *resp = gbinder_reader_read_hidl_struct(&reader, struct radio_response_info); lastRadioErr = resp->error; if (lastRadioErr != 0) goto out; switch (code) { case HIDL_SERVICE_ICC_OPEN_LOGICAL_CHANNEL_CALLBACK: gbinder_reader_read_int32(&reader, &lastIntResp); break; case HIDL_SERVICE_ICC_TRANSMIT_APDU_LOGICAL_CHANNEL_CALLBACK: { const struct icc_io_result *icc_io_res = gbinder_reader_read_hidl_struct(&reader, struct icc_io_result); // We cannot rely on the *req pointer being valid after we return lastIccIoResult.sw1 = icc_io_res->sw1; lastIccIoResult.sw2 = icc_io_res->sw2; lastIccIoResult.simResponse.data.str = strndup(icc_io_res->simResponse.data.str, icc_io_res->simResponse.len); lastIccIoResult.simResponse.len = icc_io_res->simResponse.len; lastIccIoResult.simResponse.owns_buffer = TRUE; break; } } out: g_main_loop_quit(binder_loop); return NULL; } static void cleanup_channel(int id) { GBinderLocalRequest *req = gbinder_client_new_request(client); GBinderWriter writer; gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_int32(&writer, 1000); gbinder_writer_append_int32(&writer, id); gbinder_client_transact_sync_oneway(client, HIDL_SERVICE_ICC_CLOSE_LOGICAL_CHANNEL, req); gbinder_local_request_unref(req); g_main_loop_run(binder_loop); } static void cleanup(void) { if (lastChannelId != -1) { fprintf(stderr, "Cleaning up leaked APDU channel %d\n", lastChannelId); cleanup_channel(lastChannelId); lastChannelId = -1; } } static void sighandler(int sig) { // This would trigger atexit() hooks exit(0); } static int try_open_slot(int slotId, const uint8_t *aid, uint32_t aid_len) { // First, try to connect to the HIDL service for this slot char fqname[255]; snprintf(fqname, 255, "%s/slot%d", HIDL_SERVICE_IFACE, slotId); fprintf(stderr, "Attempting to connect to %s\n", fqname); int status = 0; sm = gbinder_servicemanager_new(HIDL_SERVICE_DEVICE); remote = gbinder_remote_object_ref(gbinder_servicemanager_get_service_sync(sm, fqname, &status)); client = gbinder_client_new(remote, HIDL_SERVICE_IFACE); if (!client) { fprintf(stderr, "Failed to connect to IRadio\n"); gbinder_client_unref(client); gbinder_remote_object_unref(remote); gbinder_servicemanager_unref(sm); return -1; } response_callback = gbinder_servicemanager_new_local_object(sm, HIDL_SERVICE_IFACE_CALLBACK, radio_response_transact, NULL); GBinderLocalRequest *req = gbinder_client_new_request(client); GBinderWriter writer; gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_local_object(&writer, response_callback); gbinder_writer_append_local_object(&writer, NULL); gbinder_client_transact_sync_reply(client, HIDL_SERVICE_SET_RESPONSE_FUNCTIONS, req, &status); gbinder_local_request_unref(req); if (status < 0) { fprintf(stderr, "Failed to call IRadio::setResponseFunctions"); return -1; } // Now, try to open the AID uint8_t aid_hex[255]; euicc_hexutil_bin2hex(aid_hex, 255, aid, aid_len); req = gbinder_client_new_request(client); gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_int32(&writer, 1000); gbinder_writer_append_hidl_string_copy(&writer, aid_hex); gbinder_writer_append_int32(&writer, 0); status = gbinder_client_transact_sync_oneway(client, HIDL_SERVICE_ICC_OPEN_LOGICAL_CHANNEL, req); gbinder_local_request_unref(req); if (status < 0) { fprintf(stderr, "Failed to call IRadio::iccOpenLogicalChannel: %d\n", status); return status; } g_main_loop_run(binder_loop); if (lastRadioErr != 0) { fprintf(stderr, "Failed to open APDU logical channel: %d\n", lastRadioErr); return -lastRadioErr; } fprintf(stderr, "opened logical channel id: %d\n", lastIntResp); return lastIntResp; } static int apdu_interface_connect(struct euicc_ctx *ctx) { return 0; } static void apdu_interface_disconnect(struct euicc_ctx *ctx) { cleanup(); } static int apdu_interface_logic_channel_open(struct euicc_ctx *ctx, const uint8_t *aid, uint8_t aid_len) { // We only start to use gbinder connection here, because only now can we detect whether // a given slot is a valid eSIM slot. This way we can automatically fall back in the case // where a device has only one eSIM -- we don't want to force the user to choose in this case. int res = try_open_slot(1, aid, aid_len); if (res < 0) res = try_open_slot(2, aid, aid_len); if (res >= 0) lastChannelId = res; return res; } static void apdu_interface_logic_channel_close(struct euicc_ctx *ctx, uint8_t channel) { cleanup_channel(channel); if (lastChannelId == channel) lastChannelId = -1; // Only do this cleanup here, because on exit these objects will be destroyed anyway gbinder_client_unref(client); gbinder_remote_object_unref(remote); gbinder_servicemanager_unref(sm); } static int apdu_interface_transmit(struct euicc_ctx *ctx, uint8_t **rx, uint32_t *rx_len, const uint8_t *tx, uint32_t tx_len) { GBinderLocalRequest *req = gbinder_client_new_request(client); GBinderWriter writer; gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_int32(&writer, 1000); uint8_t tx_hex[4096] = {0}; euicc_hexutil_bin2hex(tx_hex, 4096, &tx[5], tx_len - 5); if (getenv_or_default(ENV_DEBUG, false)) fprintf(stderr, "APDU req: %s\n", tx_hex); struct sim_apdu apdu = { .sessionId = lastChannelId, .cla = tx[0], .instruction = tx[1], .p1 = tx[2], .p2 = tx[3], .p3 = tx[4], .data = { .data = {.str = (const char *)tx_hex}, .len = strlen(tx_hex) + 1, .owns_buffer = FALSE, }, }; gbinder_writer_append_struct(&writer, &apdu, &sim_apdu_t, NULL); int status = gbinder_client_transact_sync_oneway(client, HIDL_SERVICE_ICC_TRANSMIT_APDU_LOGICAL_CHANNEL, req); gbinder_local_request_unref(req); if (status < 0) { fprintf(stderr, "Failed to call IRadio::iccTransmitApduLogicalChannel: %d\n", status); return status; } g_main_loop_run(binder_loop); if (lastRadioErr != 0) { return -lastRadioErr; } if (getenv_or_default(ENV_DEBUG, false)) fprintf(stderr, "APDU resp: %d%d %d %s\n", lastIccIoResult.sw1, lastIccIoResult.sw2, lastIccIoResult.simResponse.len, lastIccIoResult.simResponse.data.str); *rx_len = lastIccIoResult.simResponse.len / 2 + 2; *rx = calloc(*rx_len, sizeof(uint8_t)); euicc_hexutil_hex2bin_r(*rx, *rx_len, lastIccIoResult.simResponse.data.str, lastIccIoResult.simResponse.len); (*rx)[*rx_len - 2] = lastIccIoResult.sw1; (*rx)[*rx_len - 1] = lastIccIoResult.sw2; // see radio_response_transact -- this is our buffer. free((void *)lastIccIoResult.simResponse.data.str); return 0; } static int libapduinterface_init(struct euicc_apdu_interface *ifstruct) { set_deprecated_env_name(ENV_DEBUG, "GBINDER_APDU_DEBUG"); ifstruct->connect = apdu_interface_connect; ifstruct->disconnect = apdu_interface_disconnect; ifstruct->logic_channel_open = apdu_interface_logic_channel_open; ifstruct->logic_channel_close = apdu_interface_logic_channel_close; ifstruct->transmit = apdu_interface_transmit; // Install cleanup routine atexit(cleanup); signal(SIGINT, sighandler); // The glib loop is detached from any client object, so create it here. binder_loop = g_main_loop_new(NULL, FALSE); return 0; } static int libapduinterface_main(int argc, char **argv) { return 0; } static void libapduinterface_fini(struct euicc_apdu_interface *ifstruct) {} const struct euicc_driver driver_apdu_gbinder_hidl = { .type = DRIVER_APDU, .name = "gbinder_hidl", .init = (int (*)(void *))libapduinterface_init, .main = libapduinterface_main, .fini = (void (*)(void *))libapduinterface_fini, }; estkme-group-lpac-c2fcf5e/driver/apdu/gbinder_hidl.h000066400000000000000000000001461504765665400226620ustar00rootroot00000000000000#pragma once #include extern const struct euicc_driver driver_apdu_gbinder_hidl; estkme-group-lpac-c2fcf5e/driver/apdu/mbim.c000066400000000000000000000263211504765665400211720ustar00rootroot00000000000000// SPDX-License-Identifier: MIT /* * Copyright (c) 2024, Frans Klaver */ #include "mbim.h" #include "mbim_helpers.h" #include #include #include #include #include #define ENV_UIM_SLOT APDU_ENV_NAME(MBIM, UIM_SLOT) #define ENV_USE_PROXY APDU_ENV_NAME(MBIM, USE_PROXY) #define ENV_DEVICE APDU_ENV_NAME(MBIM, DEVICE) struct mbim_data { const char *device_path; int last_channel_id; gboolean use_proxy; guint32 uim_slot; GMainContext *context; MbimDevice *device; }; static gboolean is_sim_available(struct mbim_data *mbim_priv) { MbimMessage *request = mbim_message_subscriber_ready_status_query_new(NULL); g_autoptr(MbimMessage) response = mbim_device_command_sync(mbim_priv->device, mbim_priv->context, request, NULL); if (!response) return FALSE; MbimSubscriberReadyState ready_state; if (!mbim_message_subscriber_ready_status_response_parse(response, &ready_state, NULL, NULL, NULL, NULL, NULL, NULL)) { return FALSE; } switch (ready_state) { case MBIM_SUBSCRIBER_READY_STATE_NO_ESIM_PROFILE: case MBIM_SUBSCRIBER_READY_STATE_INITIALIZED: return TRUE; default: return FALSE; } } static int select_sim_slot(struct mbim_data *mbim_priv) { g_autoptr(GError) error = NULL; MbimMessage *current_slot_request = mbim_message_ms_basic_connect_extensions_device_slot_mappings_query_new(NULL); g_autoptr(MbimMessage) current_slot_response = mbim_device_command_sync(mbim_priv->device, mbim_priv->context, current_slot_request, &error); if (!current_slot_response) { fprintf(stderr, "error: device didn't respond: %s\n", error->message); return -1; } guint32 current_slot_count; g_autoptr(MbimSlotArray) current_slots = NULL; if (!mbim_message_ms_basic_connect_extensions_device_slot_mappings_response_parse( current_slot_response, ¤t_slot_count, ¤t_slots, &error)) { fprintf(stderr, "error: sim select response could not be parsed: %s\n", error->message); return -1; } if (current_slot_count && current_slots[0]->slot == mbim_priv->uim_slot) { return 0; } g_autoptr(GPtrArray) new_slot_array = g_ptr_array_new_with_free_func(g_free); MbimSlot *new_slot = g_new(MbimSlot, 1); new_slot->slot = mbim_priv->uim_slot; g_ptr_array_add(new_slot_array, new_slot); MbimMessage *update_slot_request = mbim_message_ms_basic_connect_extensions_device_slot_mappings_set_new( new_slot_array->len, (const MbimSlot **)new_slot_array->pdata, &error); if (!update_slot_request) { fprintf(stderr, "error: unable to select sim slot: %s\n", error->message); return -1; } g_autoptr(MbimMessage) update_slot_response = mbim_device_command_sync(mbim_priv->device, mbim_priv->context, update_slot_request, &error); if (!update_slot_response) { fprintf(stderr, "error: device didn't respond: %s\n", error->message); return -1; } guint32 slot_count; g_autoptr(MbimSlotArray) updated_slots = NULL; if (!mbim_message_ms_basic_connect_extensions_device_slot_mappings_response_parse(update_slot_response, &slot_count, &updated_slots, &error)) { fprintf(stderr, "error: sim select response could not be parsed: %s\n", error->message); return -1; } int retries = 20; while (retries--) { if (is_sim_available(mbim_priv)) { break; } struct timespec ts = {.tv_sec = 0, .tv_nsec = 50000000}; nanosleep(&ts, NULL); } return 0; } static int apdu_interface_connect(struct euicc_ctx *ctx) { struct mbim_data *mbim_priv = ctx->apdu.interface->userdata; g_autoptr(GError) error = NULL; GFile *file; file = g_file_new_for_path(mbim_priv->device_path); mbim_priv->context = g_main_context_new(); mbim_priv->device = mbim_device_new_from_path(file, mbim_priv->context, &error); if (!mbim_priv->device) { fprintf(stderr, "error: create mbim device from path failed: %s\n", error->message); return -1; } MbimDeviceOpenFlags open_flags = MBIM_DEVICE_OPEN_FLAGS_NONE; if (mbim_priv->use_proxy) open_flags |= MBIM_DEVICE_OPEN_FLAGS_PROXY; mbim_device_open_sync(mbim_priv->device, open_flags, mbim_priv->context, &error); if (error) { fprintf(stderr, "error: open mbim device failed: %s\n", error->message); return -1; } return select_sim_slot(mbim_priv); } /* * Allocate storage in rx and copy the contents of response_data there. Also * tack the status at the end, as the MBIM protocol separates the status from * the rest of the response. */ static int copy_data_with_status(uint8_t **rx, uint32_t *rx_len, const guint8 *response_data, guint32 response_size, guint32 status) { *rx_len = response_size + 2; *rx = malloc(*rx_len); if (!*rx) return -1; memcpy(*rx, response_data, response_size); (*rx)[*rx_len - 2] = status & 0xff; (*rx)[*rx_len - 1] = (status >> 8) & 0xff; return 0; } static int mbim_apdu_interface_transmit(struct euicc_ctx *ctx, uint8_t **rx, uint32_t *rx_len, const uint8_t *tx, uint32_t tx_len) { struct mbim_data *mbim_priv = ctx->apdu.interface->userdata; g_autoptr(GError) error = NULL; MbimMessage *request = mbim_message_ms_uicc_low_level_access_apdu_set_new( mbim_priv->last_channel_id, MBIM_UICC_SECURE_MESSAGING_NONE, MBIM_UICC_CLASS_BYTE_TYPE_INTER_INDUSTRY, tx_len, tx, &error); if (!request) { fprintf(stderr, "error: creating apdu message failed: %s\n", error->message); return -1; } g_autoptr(MbimMessage) response = mbim_device_command_sync(mbim_priv->device, mbim_priv->context, request, &error); if (!response) { fprintf(stderr, "error: no apdu response received: %s\n", error->message); return -1; } guint32 status = 0; guint32 response_size = 0; const guint8 *response_data = NULL; if (!mbim_message_ms_uicc_low_level_access_apdu_response_parse(response, &status, &response_size, &response_data, &error)) { fprintf(stderr, "error: unable to parse apdu response: %s\n", error->message); return -1; } return copy_data_with_status(rx, rx_len, response_data, response_size, status); } static int mbim_apdu_interface_logic_channel_open(struct euicc_ctx *ctx, const uint8_t *aid, uint8_t aid_len) { struct mbim_data *mbim_priv = ctx->apdu.interface->userdata; g_autoptr(GError) error = NULL; guint8 channel_id; MbimMessage *request = mbim_message_ms_uicc_low_level_access_open_channel_set_new(aid_len, aid, 0, 1, &error); if (!request) { fprintf(stderr, "error: creating channel message failed: %s\n", error->message); return -1; } g_autoptr(MbimMessage) response = mbim_device_command_sync(mbim_priv->device, mbim_priv->context, request, &error); if (!response) { fprintf(stderr, "error: no channel response received: %s\n", error->message); return -1; } guint32 status = 0; guint32 channel = -1; guint32 response_size = 0; const guint8 *response_data = NULL; if (!mbim_message_ms_uicc_low_level_access_open_channel_response_parse(response, &status, &channel, &response_size, &response_data, &error)) { fprintf(stderr, "error: unable to parse channel response: %s\n", error->message); return -1; } mbim_priv->last_channel_id = channel; return channel; } static void mbim_apdu_interface_logic_channel_close(struct euicc_ctx *ctx, uint8_t channel) { struct mbim_data *mbim_priv = ctx->apdu.interface->userdata; g_autoptr(GError) error = NULL; MbimMessage *request = mbim_message_ms_uicc_low_level_access_close_channel_set_new(channel, 1, &error); if (!request) { fprintf(stderr, "error: creating channel message failed: %s\n", error->message); return; } g_autoptr(MbimMessage) response = mbim_device_command_sync(mbim_priv->device, mbim_priv->context, request, &error); if (!response) { fprintf(stderr, "error: no channel response received: %s\n", error->message); return; } guint32 status = 0; if (!mbim_message_ms_uicc_low_level_access_close_channel_response_parse(response, &status, &error)) { fprintf(stderr, "error: unable to parse channel response: %s\n", error->message); return; } if (channel == mbim_priv->last_channel_id) mbim_priv->last_channel_id = -1; } static void mbim_apdu_interface_disconnect(struct euicc_ctx *ctx) { struct mbim_data *mbim_priv = ctx->apdu.interface->userdata; g_autoptr(GError) error = NULL; if (mbim_priv->last_channel_id > 0) { fprintf(stderr, "Cleaning up leaked APDU channel %d\n", mbim_priv->last_channel_id); mbim_apdu_interface_logic_channel_close(NULL, mbim_priv->last_channel_id); mbim_priv->last_channel_id = -1; } mbim_device_close_sync(mbim_priv->device, mbim_priv->context, &error); g_main_context_unref(mbim_priv->context); mbim_priv->context = NULL; } static int libapduinterface_init(struct euicc_apdu_interface *ifstruct) { set_deprecated_env_name(ENV_UIM_SLOT, "UIM_SLOT"); set_deprecated_env_name(ENV_USE_PROXY, "MBIM_USE_PROXY"); set_deprecated_env_name(ENV_DEVICE, "MBIM_DEVICE"); struct mbim_data *mbim_priv; guint32 uim_slot = getenv_or_default(ENV_UIM_SLOT, (int)1); /* * We're using the same UIM_SLOT environment variable as the QMI backends. * QMI uses 1-based indexing for the sim slots. MBIM uses 0-based indexing, * so account for that. */ if (uim_slot == 0) { fprintf(stderr, "error: Invalid " ENV_UIM_SLOT "\n"); return -1; } uim_slot--; mbim_priv = malloc(sizeof(struct mbim_data)); if (!mbim_priv) { fprintf(stderr, "Failed allocating memory\n"); return -1; } mbim_priv->uim_slot = uim_slot; mbim_priv->use_proxy = getenv_or_default(ENV_USE_PROXY, false); mbim_priv->device_path = getenv_or_default(ENV_DEVICE, "/dev/cdc-wdm0"); memset(ifstruct, 0, sizeof(struct euicc_apdu_interface)); ifstruct->connect = apdu_interface_connect; ifstruct->disconnect = mbim_apdu_interface_disconnect; ifstruct->logic_channel_open = mbim_apdu_interface_logic_channel_open; ifstruct->logic_channel_close = mbim_apdu_interface_logic_channel_close; ifstruct->transmit = mbim_apdu_interface_transmit; ifstruct->userdata = mbim_priv; return 0; } static int libapduinterface_main(int argc, char **argv) { return 0; } static void libapduinterface_fini(struct euicc_apdu_interface *ifstruct) { g_free(ifstruct->userdata); } const struct euicc_driver driver_apdu_mbim = { .type = DRIVER_APDU, .name = "mbim", .init = (int (*)(void *))libapduinterface_init, .main = libapduinterface_main, .fini = (void (*)(void *))libapduinterface_fini, }; estkme-group-lpac-c2fcf5e/driver/apdu/mbim.h000066400000000000000000000003041504765665400211700ustar00rootroot00000000000000// SPDX-License-Identifier: MIT /* * Copyright (c) 2024, Frans Klaver */ #pragma once #include extern const struct euicc_driver driver_apdu_mbim; estkme-group-lpac-c2fcf5e/driver/apdu/mbim_helpers.c000066400000000000000000000050051504765665400227100ustar00rootroot00000000000000// SPDX-License-Identifier: MIT /* Copyright (c) 2024, Frans Klaver */ #include "mbim_helpers.h" #include static void async_result_ready(GObject *source_object, GAsyncResult *res, gpointer user_data) { GAsyncResult **result_out = user_data; g_assert(*result_out == NULL); *result_out = g_object_ref(res); } MbimDevice *mbim_device_new_from_path(GFile *file, GMainContext *context, GError **error) { g_autoptr(GMainContextPusher) pusher = NULL; g_autoptr(GAsyncResult) result = NULL; g_autofree gchar *id = NULL; pusher = g_main_context_pusher_new(context); id = g_file_get_path(file); if (id) mbim_device_new(file, NULL, async_result_ready, &result); while (!result) g_main_context_iteration(context, TRUE); return mbim_device_new_finish(result, error); } gboolean mbim_device_open_sync(MbimDevice *device, MbimDeviceOpenFlags open_flags, GMainContext *context, GError **error) { g_autoptr(GMainContextPusher) pusher = NULL; g_autoptr(GAsyncResult) result = NULL; pusher = g_main_context_pusher_new(context); mbim_device_open_full(device, open_flags, 15, NULL, async_result_ready, &result); while (!result) g_main_context_iteration(context, TRUE); return mbim_device_open_finish(device, result, error); } MbimMessage *mbim_device_command_sync(MbimDevice *device, GMainContext *context, MbimMessage *request, GError **error) { g_autoptr(GMainContextPusher) pusher = NULL; g_autoptr(GAsyncResult) result = NULL; pusher = g_main_context_pusher_new(context); mbim_device_command(device, request, 10, NULL, async_result_ready, &result); mbim_message_unref(request); while (result == NULL) g_main_context_iteration(context, TRUE); MbimMessage *response = mbim_device_command_finish(device, result, error); if (!response) { return NULL; } if (!mbim_message_response_get_result(response, MBIM_MESSAGE_TYPE_COMMAND_DONE, error)) { return NULL; } return response; } gboolean mbim_device_close_sync(MbimDevice *device, GMainContext *context, GError **error) { g_autoptr(GMainContextPusher) pusher = NULL; g_autoptr(GAsyncResult) result = NULL; pusher = g_main_context_pusher_new(context); mbim_device_close(device, 20, NULL, async_result_ready, &result); while (result == NULL) g_main_context_iteration(context, TRUE); return mbim_device_close_finish(device, result, error); } estkme-group-lpac-c2fcf5e/driver/apdu/mbim_helpers.h000066400000000000000000000011331504765665400227130ustar00rootroot00000000000000// SPDX-License-Identifier: MIT /* * Copyright (c) 2024, Frans Klaver */ #pragma once #include MbimDevice *mbim_device_new_from_path(GFile *file, GMainContext *context, GError **error); gboolean mbim_device_open_sync(MbimDevice *device, MbimDeviceOpenFlags open_flags, GMainContext *context, GError **error); gboolean mbim_device_close_sync(MbimDevice *device, GMainContext *context, GError **error); MbimMessage *mbim_device_command_sync(MbimDevice *device, GMainContext *context, MbimMessage *request, GError **error); estkme-group-lpac-c2fcf5e/driver/apdu/pcsc.c000066400000000000000000000240221504765665400211720ustar00rootroot00000000000000#include "pcsc.h" #include #include #include #include #include #include #include #ifdef _WIN32 # include "pcsc_win32.h" # include #else # include # include #endif #define ENV_DRV_IFID APDU_ENV_NAME(PCSC, DRV_IFID) #define ENV_DRV_NAME APDU_ENV_NAME(PCSC, DRV_NAME) #define ENV_DRV_IGNORE_NAME APDU_ENV_NAME(PCSC, DRV_IGNORE_NAME) #define EUICC_INTERFACE_BUFSZ 264 // #define APDU_ST33_MAGIC "\x90\xBD\x36\xBB\x00" #define APDU_TERMINAL_CAPABILITIES "\x80\xAA\x00\x00\x0A\xA9\x08\x81\x00\x82\x01\x01\x83\x01\x07" #define APDU_OPENLOGICCHANNEL "\x00\x70\x00\x00\x01" #define APDU_CLOSELOGICCHANNEL "\x00\x70\x80\xFF\x00" #define APDU_SELECT_HEADER "\x00\xA4\x04\x00\xFF" static SCARDCONTEXT pcsc_ctx; static SCARDHANDLE pcsc_hCard; static LPSTR pcsc_mszReaders; static void pcsc_error(const char *method, const int32_t code) { fprintf(stderr, "%s failed: %08X (%s)\n", method, code, pcsc_stringify_error(code)); } static bool is_ignored_reader_name(const char *reader) { char *value = getenv(ENV_DRV_IGNORE_NAME); if (value == NULL) return false; const char *token = NULL; for (token = strtok(value, ";"); token != NULL; token = strtok(NULL, ";")) { if (strstr(reader, token) == NULL) continue; return true; // reader name is in ignore list, skip } return false; } static int pcsc_ctx_open(void) { int ret; DWORD dwReaders; pcsc_ctx = 0; pcsc_hCard = 0; pcsc_mszReaders = NULL; ret = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &pcsc_ctx); if (ret != SCARD_S_SUCCESS) { pcsc_error("SCardEstablishContext()", ret); return -1; } #ifdef SCARD_AUTOALLOCATE dwReaders = SCARD_AUTOALLOCATE; ret = SCardListReaders(pcsc_ctx, NULL, (LPSTR)&pcsc_mszReaders, &dwReaders); #else // macOS does not support SCARD_AUTOALLOCATE, so we need to call SCardListReaders twice. // First call to get the size of the buffer, second call to get the actual data. ret = SCardListReaders(pcsc_ctx, NULL, NULL, &dwReaders); if (ret != SCARD_S_SUCCESS) { pcsc_error("SCardListReaders()", ret); return -1; } pcsc_mszReaders = malloc(sizeof(char) * dwReaders); if (pcsc_mszReaders == NULL) { fprintf(stderr, "malloc: not enough memory\n"); return -1; } ret = SCardListReaders(pcsc_ctx, NULL, pcsc_mszReaders, &dwReaders); #endif if (ret != SCARD_S_SUCCESS) { pcsc_error("SCardListReaders()", ret); return -1; } return 0; } static int pcsc_iter_reader(int (*callback)(int index, const char *reader, void *userdata), void *userdata) { int ret; LPSTR psReader; psReader = pcsc_mszReaders; for (int i = 0, n = 0;; i++) { char *p = pcsc_mszReaders + i; if (*p == '\0') { ret = callback(n, psReader, userdata); if (ret < 0) return -1; if (ret > 0) return 0; if (*(p + 1) == '\0') { break; } psReader = p + 1; n++; } } return -1; } static int pcsc_open_hCard_iter(int index, const char *reader, void *userdata) { DWORD dwActiveProtocol; const int id = getenv_or_default(ENV_DRV_IFID, (int)-1); if (id != -1 && id != index) { const char *part_name = getenv(ENV_DRV_NAME); if (strstr(reader, part_name) == NULL) { return 0; } } if (is_ignored_reader_name(reader)) { return 0; // skip ignored reader names } const int ret = SCardConnect(pcsc_ctx, reader, SCARD_SHARE_EXCLUSIVE, SCARD_PROTOCOL_T0, &pcsc_hCard, &dwActiveProtocol); if (ret != SCARD_S_SUCCESS) { pcsc_error("SCardConnect()", ret); // see if (ret == SCARD_E_SHARING_VIOLATION) return 0; // skip return -1; } return 1; } static int pcsc_open_hCard(void) { return pcsc_iter_reader(pcsc_open_hCard_iter, NULL); } static void pcsc_close(void) { if (pcsc_mszReaders) { // macOS does not support SCARD_AUTOALLOCATE, so we need to free the buffer manually. #ifdef SCARD_AUTOALLOCATE SCardFreeMemory(pcsc_ctx, pcsc_mszReaders); #else // on macOS, pcsc_mszReaders is allocated by malloc() free(pcsc_mszReaders); #endif } if (pcsc_hCard) { SCardDisconnect(pcsc_hCard, SCARD_UNPOWER_CARD); } if (pcsc_ctx) { SCardReleaseContext(pcsc_ctx); } pcsc_ctx = 0; pcsc_hCard = 0; pcsc_mszReaders = NULL; } static int pcsc_transmit_lowlevel(uint8_t *rx, uint32_t *rx_len, const uint8_t *tx, const uint8_t tx_len) { int ret; DWORD rx_len_merged; rx_len_merged = *rx_len; ret = SCardTransmit(pcsc_hCard, SCARD_PCI_T0, tx, tx_len, NULL, rx, &rx_len_merged); if (ret != SCARD_S_SUCCESS) { pcsc_error("SCardTransmit()", ret); return -1; } *rx_len = rx_len_merged; return 0; } static void pcsc_logic_channel_close(uint8_t channel) { uint8_t tx[sizeof(APDU_CLOSELOGICCHANNEL) - 1]; uint8_t rx[EUICC_INTERFACE_BUFSZ]; uint32_t rx_len; memcpy(tx, APDU_CLOSELOGICCHANNEL, sizeof(tx)); tx[3] = channel; rx_len = sizeof(rx); pcsc_transmit_lowlevel(rx, &rx_len, tx, sizeof(tx)); } static int pcsc_logic_channel_open(const uint8_t *aid, uint8_t aid_len) { int channel = 0; uint8_t tx[EUICC_INTERFACE_BUFSZ]; uint8_t *tx_wptr; uint8_t rx[EUICC_INTERFACE_BUFSZ]; uint32_t rx_len; if (aid_len > 32) { goto err; } rx_len = sizeof(rx); if (pcsc_transmit_lowlevel(rx, &rx_len, (const uint8_t *)APDU_OPENLOGICCHANNEL, sizeof(APDU_OPENLOGICCHANNEL) - 1) < 0) { goto err; } if (rx_len != 3) { goto err; } if ((rx[1] & 0xF0) != 0x90) { goto err; } channel = rx[0]; tx_wptr = tx; memcpy(tx_wptr, APDU_SELECT_HEADER, sizeof(APDU_SELECT_HEADER) - 1); tx_wptr += sizeof(APDU_SELECT_HEADER) - 1; memcpy(tx_wptr, aid, aid_len); tx_wptr += aid_len; tx[0] = (tx[0] & 0xF0) | channel; tx[4] = aid_len; rx_len = sizeof(rx); if (pcsc_transmit_lowlevel(rx, &rx_len, tx, tx_wptr - tx) < 0) { goto err; } if (rx_len < 2) { goto err; } switch (rx[rx_len - 2]) { case 0x90: case 0x61: return channel; default: goto err; } err: if (channel) { pcsc_logic_channel_close(channel); } return -1; } static int apdu_interface_connect(struct euicc_ctx *ctx) { uint8_t rx[EUICC_INTERFACE_BUFSZ]; uint32_t rx_len; if (pcsc_open_hCard() < 0) { return -1; } rx_len = sizeof(rx); pcsc_transmit_lowlevel(rx, &rx_len, (const uint8_t *)APDU_TERMINAL_CAPABILITIES, sizeof(APDU_TERMINAL_CAPABILITIES) - 1); return 0; } static void apdu_interface_disconnect(struct euicc_ctx *ctx) { pcsc_close(); } static int apdu_interface_transmit(struct euicc_ctx *ctx, uint8_t **rx, uint32_t *rx_len, const uint8_t *tx, uint32_t tx_len) { *rx = malloc(EUICC_INTERFACE_BUFSZ); if (!*rx) { fprintf(stderr, "SCardTransmit() RX buffer alloc failed\n"); return -1; } *rx_len = EUICC_INTERFACE_BUFSZ; if (pcsc_transmit_lowlevel(*rx, rx_len, tx, tx_len) < 0) { free(*rx); *rx_len = 0; return -1; } return 0; } static int apdu_interface_logic_channel_open(struct euicc_ctx *ctx, const uint8_t *aid, uint8_t aid_len) { return pcsc_logic_channel_open(aid, aid_len); } static void apdu_interface_logic_channel_close(struct euicc_ctx *ctx, uint8_t channel) { pcsc_logic_channel_close(channel); } static int pcsc_list_iter(int index, const char *reader, void *userdata) { cJSON *json = userdata; cJSON *jreader; char index_str[16]; snprintf(index_str, sizeof(index_str), "%d", index); jreader = cJSON_CreateObject(); if (!jreader) { return -1; } if (!cJSON_AddStringOrNullToObject(jreader, "env", index_str)) { return -1; } if (!cJSON_AddStringOrNullToObject(jreader, "name", reader)) { return -1; } if (!cJSON_AddItemToArray(json, jreader)) { return -1; } return 0; } static int libapduinterface_init(struct euicc_apdu_interface *ifstruct) { set_deprecated_env_name(ENV_DRV_IFID, "DRIVER_IFID"); set_deprecated_env_name(ENV_DRV_NAME, "DRIVER_NAME"); memset(ifstruct, 0, sizeof(struct euicc_apdu_interface)); if (pcsc_ctx_open() < 0) { return -1; } ifstruct->connect = apdu_interface_connect; ifstruct->disconnect = apdu_interface_disconnect; ifstruct->logic_channel_open = apdu_interface_logic_channel_open; ifstruct->logic_channel_close = apdu_interface_logic_channel_close; ifstruct->transmit = apdu_interface_transmit; return 0; } static int libapduinterface_main(int argc, char **argv) { if (argc < 2) { fprintf(stderr, "Usage: %s \n", argv[0]); return -1; } if (strcmp(argv[1], "list") == 0) { cJSON *payload; cJSON *data; payload = cJSON_CreateObject(); if (!payload) { return -1; } if (!cJSON_AddStringOrNullToObject(payload, "env", ENV_DRV_IFID)) { return -1; } data = cJSON_CreateArray(); if (!data) { return -1; } pcsc_iter_reader(pcsc_list_iter, data); if (!cJSON_AddItemToObject(payload, "data", data)) { return -1; } json_print("driver", payload); return 0; } return 0; } static void libapduinterface_fini(struct euicc_apdu_interface *ifstruct) {} const struct euicc_driver driver_apdu_pcsc = { .type = DRIVER_APDU, .name = "pcsc", .init = (int (*)(void *))libapduinterface_init, .main = libapduinterface_main, .fini = (void (*)(void *))libapduinterface_fini, }; estkme-group-lpac-c2fcf5e/driver/apdu/pcsc.h000066400000000000000000000001361504765665400211770ustar00rootroot00000000000000#pragma once #include extern const struct euicc_driver driver_apdu_pcsc; estkme-group-lpac-c2fcf5e/driver/apdu/pcsc_win32.c000066400000000000000000000067401504765665400222230ustar00rootroot00000000000000#include "pcsc_win32.h" #define PCSC_ERROR_CASE(NAME) \ case NAME: \ return #NAME const char *pcsc_stringify_error(const LONG err) { switch (err) { PCSC_ERROR_CASE(SCARD_S_SUCCESS); PCSC_ERROR_CASE(SCARD_F_INTERNAL_ERROR); PCSC_ERROR_CASE(SCARD_E_CANCELLED); PCSC_ERROR_CASE(SCARD_E_INVALID_HANDLE); PCSC_ERROR_CASE(SCARD_E_INVALID_PARAMETER); PCSC_ERROR_CASE(SCARD_E_INVALID_TARGET); PCSC_ERROR_CASE(SCARD_E_NO_MEMORY); PCSC_ERROR_CASE(SCARD_F_WAITED_TOO_LONG); PCSC_ERROR_CASE(SCARD_E_INSUFFICIENT_BUFFER); PCSC_ERROR_CASE(SCARD_E_UNKNOWN_READER); PCSC_ERROR_CASE(SCARD_E_TIMEOUT); PCSC_ERROR_CASE(SCARD_E_SHARING_VIOLATION); PCSC_ERROR_CASE(SCARD_E_NO_SMARTCARD); PCSC_ERROR_CASE(SCARD_E_UNKNOWN_CARD); PCSC_ERROR_CASE(SCARD_E_CANT_DISPOSE); PCSC_ERROR_CASE(SCARD_E_PROTO_MISMATCH); PCSC_ERROR_CASE(SCARD_E_NOT_READY); PCSC_ERROR_CASE(SCARD_E_INVALID_VALUE); PCSC_ERROR_CASE(SCARD_E_SYSTEM_CANCELLED); PCSC_ERROR_CASE(SCARD_F_COMM_ERROR); PCSC_ERROR_CASE(SCARD_F_UNKNOWN_ERROR); PCSC_ERROR_CASE(SCARD_E_INVALID_ATR); PCSC_ERROR_CASE(SCARD_E_NOT_TRANSACTED); PCSC_ERROR_CASE(SCARD_E_READER_UNAVAILABLE); PCSC_ERROR_CASE(SCARD_P_SHUTDOWN); PCSC_ERROR_CASE(SCARD_E_PCI_TOO_SMALL); PCSC_ERROR_CASE(SCARD_E_READER_UNSUPPORTED); PCSC_ERROR_CASE(SCARD_E_DUPLICATE_READER); PCSC_ERROR_CASE(SCARD_E_CARD_UNSUPPORTED); PCSC_ERROR_CASE(SCARD_E_NO_SERVICE); PCSC_ERROR_CASE(SCARD_E_SERVICE_STOPPED); PCSC_ERROR_CASE(SCARD_E_UNEXPECTED); PCSC_ERROR_CASE(SCARD_E_ICC_INSTALLATION); PCSC_ERROR_CASE(SCARD_E_ICC_CREATEORDER); PCSC_ERROR_CASE(SCARD_E_UNSUPPORTED_FEATURE); PCSC_ERROR_CASE(SCARD_E_DIR_NOT_FOUND); PCSC_ERROR_CASE(SCARD_E_FILE_NOT_FOUND); PCSC_ERROR_CASE(SCARD_E_NO_DIR); PCSC_ERROR_CASE(SCARD_E_NO_FILE); PCSC_ERROR_CASE(SCARD_E_NO_ACCESS); PCSC_ERROR_CASE(SCARD_E_WRITE_TOO_MANY); PCSC_ERROR_CASE(SCARD_E_BAD_SEEK); PCSC_ERROR_CASE(SCARD_E_INVALID_CHV); PCSC_ERROR_CASE(SCARD_E_UNKNOWN_RES_MNG); PCSC_ERROR_CASE(SCARD_E_NO_SUCH_CERTIFICATE); PCSC_ERROR_CASE(SCARD_E_CERTIFICATE_UNAVAILABLE); PCSC_ERROR_CASE(SCARD_E_NO_READERS_AVAILABLE); PCSC_ERROR_CASE(SCARD_E_COMM_DATA_LOST); PCSC_ERROR_CASE(SCARD_E_NO_KEY_CONTAINER); PCSC_ERROR_CASE(SCARD_E_SERVER_TOO_BUSY); PCSC_ERROR_CASE(SCARD_E_PIN_CACHE_EXPIRED); PCSC_ERROR_CASE(SCARD_E_NO_PIN_CACHE); PCSC_ERROR_CASE(SCARD_E_READ_ONLY_CARD); PCSC_ERROR_CASE(SCARD_W_UNSUPPORTED_CARD); PCSC_ERROR_CASE(SCARD_W_UNRESPONSIVE_CARD); PCSC_ERROR_CASE(SCARD_W_UNPOWERED_CARD); PCSC_ERROR_CASE(SCARD_W_RESET_CARD); PCSC_ERROR_CASE(SCARD_W_REMOVED_CARD); PCSC_ERROR_CASE(SCARD_W_SECURITY_VIOLATION); PCSC_ERROR_CASE(SCARD_W_WRONG_CHV); PCSC_ERROR_CASE(SCARD_W_CHV_BLOCKED); PCSC_ERROR_CASE(SCARD_W_EOF); PCSC_ERROR_CASE(SCARD_W_CANCELLED_BY_USER); PCSC_ERROR_CASE(SCARD_W_CARD_NOT_AUTHENTICATED); PCSC_ERROR_CASE(SCARD_W_CACHE_ITEM_NOT_FOUND); PCSC_ERROR_CASE(SCARD_W_CACHE_ITEM_STALE); PCSC_ERROR_CASE(ERROR_IO_DEVICE); PCSC_ERROR_CASE(ERROR_BROKEN_PIPE); default: return "Unknown error"; } } #undef PCSC_ERROR_CASE estkme-group-lpac-c2fcf5e/driver/apdu/pcsc_win32.h000066400000000000000000000001461504765665400222220ustar00rootroot00000000000000#pragma once #ifdef _WIN32 # include const char *pcsc_stringify_error(LONG); #endif estkme-group-lpac-c2fcf5e/driver/apdu/qmi.c000066400000000000000000000223161504765665400210340ustar00rootroot00000000000000// SPDX-License-Identifier: MIT /* * Copyright (c) 2024, Robert Marko */ #include "qmi.h" #include "qmi_common.h" #include #include #include static gboolean is_sim_available(struct qmi_data *qmi_priv) { g_autoptr(GError) error = NULL; g_autoptr(QmiMessageUimGetCardStatusOutput) card_status_output = NULL; GArray *cards = NULL; guint i, j; // Get card status card_status_output = qmi_client_uim_get_card_status_sync(qmi_priv->uimClient, qmi_priv->context, &error); if (!card_status_output) { fprintf(stderr, "error: get card status failed: %s\n", error->message); return FALSE; } // Check if the operation was successful if (!qmi_message_uim_get_card_status_output_get_result(card_status_output, &error)) { fprintf(stderr, "error: get card status operation failed: %s\n", error->message); return FALSE; } // Get card status details if (!qmi_message_uim_get_card_status_output_get_card_status(card_status_output, NULL, // index_gw_primary NULL, // index_1x_primary NULL, // index_gw_secondary NULL, // index_1x_secondary &cards, &error)) { fprintf(stderr, "error: get card status details failed: %s\n", error->message); return FALSE; } // Check if any card is present and has a USIM application that is ready for (i = 0; i < cards->len; i++) { QmiMessageUimGetCardStatusOutputCardStatusCardsElement *card_element; card_element = &g_array_index(cards, QmiMessageUimGetCardStatusOutputCardStatusCardsElement, i); // Check applications on this card for (j = 0; j < card_element->applications->len; j++) { QmiMessageUimGetCardStatusOutputCardStatusCardsElementApplicationsElementV2 *app_element; app_element = &g_array_index(card_element->applications, QmiMessageUimGetCardStatusOutputCardStatusCardsElementApplicationsElementV2, j); // Check if this is a USIM application and if it's ready if (app_element->type == QMI_UIM_CARD_APPLICATION_TYPE_USIM && app_element->state == QMI_UIM_CARD_APPLICATION_STATE_READY) { return TRUE; } } } return FALSE; } static gboolean select_sim_slot(struct qmi_data *qmi_priv) { g_autoptr(GError) error = NULL; g_autoptr(QmiMessageUimGetSlotStatusOutput) slot_status_output = NULL; g_autoptr(QmiMessageUimSwitchSlotInput) switch_slot_input = NULL; g_autoptr(QmiMessageUimSwitchSlotOutput) switch_slot_output = NULL; g_autoptr(QmiMessageUimGetCardStatusOutput) card_status_output = NULL; GArray *physical_slot_status = NULL; guint8 active_slot = 0; guint8 target_slot; guint i; int retries; // Get the target slot (1-based indexing) target_slot = qmi_priv->uimSlot; // Get current slot status slot_status_output = qmi_client_uim_get_slot_status_sync(qmi_priv->uimClient, qmi_priv->context, &error); if (!slot_status_output) { fprintf(stderr, "error: get slot status failed: %s\n", error->message); return FALSE; } // Check if the operation was successful if (!qmi_message_uim_get_slot_status_output_get_result(slot_status_output, &error)) { // Some older devices do not support the GetSlotStatusRequest QMI command if (error->code == QMI_PROTOCOL_ERROR_NOT_SUPPORTED) { return TRUE; } fprintf(stderr, "error: get slot status operation failed: %s\n", error->message); return FALSE; } // Get physical slot status if (!qmi_message_uim_get_slot_status_output_get_physical_slot_status(slot_status_output, &physical_slot_status, &error)) { fprintf(stderr, "error: get physical slot status failed: %s\n", error->message); return FALSE; } // Find the active slot active_slot = 0; for (i = 0; i < physical_slot_status->len; i++) { QmiPhysicalSlotStatusSlot *element; element = &g_array_index(physical_slot_status, QmiPhysicalSlotStatusSlot, i); if (element->physical_slot_status == QMI_UIM_SLOT_STATE_ACTIVE) { active_slot = i + 1; // 1-based indexing break; } } // If the active slot is not the target slot, switch to the target slot if (active_slot != target_slot) { // Create switch slot input switch_slot_input = qmi_message_uim_switch_slot_input_new(); if (!qmi_message_uim_switch_slot_input_set_logical_slot(switch_slot_input, 1, &error)) { fprintf(stderr, "error: set logical slot failed: %s\n", error->message); return FALSE; } // Set the physical slot (use the logical slot number as physical slot) if (!qmi_message_uim_switch_slot_input_set_physical_slot(switch_slot_input, target_slot, &error)) { fprintf(stderr, "error: set physical slot failed: %s\n", error->message); return FALSE; } // Switch to the target slot switch_slot_output = qmi_client_uim_switch_slot_sync(qmi_priv->uimClient, switch_slot_input, qmi_priv->context, &error); if (!switch_slot_output) { fprintf(stderr, "error: switch slot failed: %s\n", error->message); return FALSE; } // Check if the operation was successful if (!qmi_message_uim_switch_slot_output_get_result(switch_slot_output, &error)) { fprintf(stderr, "error: switch slot operation failed: %s\n", error->message); return FALSE; } // Wait for SIM to be available for (retries = 0; retries < 20; retries++) { if (is_sim_available(qmi_priv)) { return TRUE; } // Wait a bit and retry g_usleep(500000); // 0.5 seconds } fprintf(stderr, "error: SIM not available after switching slot\n"); return FALSE; } return TRUE; } static int apdu_interface_connect(struct euicc_ctx *ctx) { struct qmi_data *qmi_priv = ctx->apdu.interface->userdata; g_autoptr(GError) error = NULL; QmiDevice *device = NULL; QmiClient *client = NULL; const char *device_path = getenv(ENV_DEVICE); GFile *file; if (device_path == NULL) { fprintf(stderr, "No QMI device path specified!\n"); return -1; } file = g_file_new_for_path(device_path); qmi_priv->context = g_main_context_new(); device = qmi_device_new_from_path(file, qmi_priv->context, &error); if (!device) { fprintf(stderr, "error: create QMI device from path failed: %s\n", error->message); return -1; } qmi_device_open_sync(device, QMI_DEVICE_OPEN_FLAGS_PROXY, qmi_priv->context, &error); if (error) { fprintf(stderr, "error: open QMI device failed: %s\n", error->message); return -1; } client = qmi_device_allocate_client_sync(device, qmi_priv->context, &error); if (!client) { fprintf(stderr, "error: allocate QMI client failed: %s\n", error->message); return -1; } qmi_priv->uimClient = QMI_CLIENT_UIM(client); if (select_sim_slot(qmi_priv) < 0) { fprintf(stderr, "error: select SIM slot failed\n"); return -1; } // In QMI mode, we need to keep the SIM slot set to 1, because once the // configured slot becomes active, it will be assigned as slot 1. qmi_priv->uimSlot = 1; return 0; } static int libapduinterface_init(struct euicc_apdu_interface *ifstruct) { set_deprecated_env_name(ENV_UIM_SLOT, "UIM_SLOT"); set_deprecated_env_name(ENV_DEVICE, "QMI_DEVICE"); struct qmi_data *qmi_priv; qmi_priv = calloc(1, sizeof(struct qmi_data)); if (!qmi_priv) { fprintf(stderr, "Failed allocating memory\n"); return -1; } memset(ifstruct, 0, sizeof(struct euicc_apdu_interface)); ifstruct->connect = apdu_interface_connect; ifstruct->disconnect = qmi_apdu_interface_disconnect; ifstruct->logic_channel_open = qmi_apdu_interface_logic_channel_open; ifstruct->logic_channel_close = qmi_apdu_interface_logic_channel_close; ifstruct->transmit = qmi_apdu_interface_transmit; /* * Allow the user to select the SIM card slot via environment variable. * Use the primary SIM slot if not set. */ qmi_priv->uimSlot = getenv_or_default(ENV_UIM_SLOT, (int)1); ifstruct->userdata = qmi_priv; return 0; } static int libapduinterface_main(int argc, char **argv) { return 0; } static void libapduinterface_fini(struct euicc_apdu_interface *ifstruct) { struct qmi_data *qmi_priv = ifstruct->userdata; qmi_cleanup(qmi_priv); free(qmi_priv); } const struct euicc_driver driver_apdu_qmi = { .type = DRIVER_APDU, .name = "qmi", .init = (int (*)(void *))libapduinterface_init, .main = libapduinterface_main, .fini = (void (*)(void *))libapduinterface_fini, }; estkme-group-lpac-c2fcf5e/driver/apdu/qmi.h000066400000000000000000000003021504765665400210300ustar00rootroot00000000000000// SPDX-License-Identifier: MIT /* * Copyright (c) 2024, Robert Marko */ #pragma once #include extern const struct euicc_driver driver_apdu_qmi; estkme-group-lpac-c2fcf5e/driver/apdu/qmi_common.c000066400000000000000000000126521504765665400224060ustar00rootroot00000000000000// SPDX-License-Identifier: MIT /* * Copyright (c) 2024, Luca Weiss */ #include "qmi_common.h" #include int qmi_apdu_interface_transmit(struct euicc_ctx *ctx, uint8_t **rx, uint32_t *rx_len, const uint8_t *tx, uint32_t tx_len) { struct qmi_data *qmi_priv = ctx->apdu.interface->userdata; g_autoptr(GError) error = NULL; g_autoptr(GArray) apdu_data = NULL; /* Convert tx into request GArray */ apdu_data = g_array_new(FALSE, FALSE, sizeof(guint8)); for (uint32_t i = 0; i < tx_len; i++) g_array_append_val(apdu_data, tx[i]); QmiMessageUimSendApduInput *input; input = qmi_message_uim_send_apdu_input_new(); qmi_message_uim_send_apdu_input_set_slot(input, qmi_priv->uimSlot, NULL); qmi_message_uim_send_apdu_input_set_channel_id(input, qmi_priv->lastChannelId, NULL); qmi_message_uim_send_apdu_input_set_apdu(input, apdu_data, NULL); QmiMessageUimSendApduOutput *output; output = qmi_client_uim_send_apdu_sync(qmi_priv->uimClient, input, qmi_priv->context, &error); qmi_message_uim_send_apdu_input_unref(input); if (!qmi_message_uim_send_apdu_output_get_result(output, &error)) { fprintf(stderr, "error: send apdu operation failed: %s\n", error->message); return -1; } GArray *apdu_res = NULL; if (!qmi_message_uim_send_apdu_output_get_apdu_response(output, &apdu_res, &error)) { fprintf(stderr, "error: get apdu response operation failed: %s\n", error->message); return -1; } /* Convert response GArray into rx */ *rx_len = apdu_res->len; *rx = malloc(*rx_len); if (!*rx) return -1; for (guint i = 0; i < apdu_res->len; i++) (*rx)[i] = apdu_res->data[i]; qmi_message_uim_send_apdu_output_unref(output); return 0; } int qmi_apdu_interface_logic_channel_open(struct euicc_ctx *ctx, const uint8_t *aid, uint8_t aid_len) { struct qmi_data *qmi_priv = ctx->apdu.interface->userdata; g_autoptr(GError) error = NULL; guint8 channel_id; GArray *aid_data = g_array_new(FALSE, FALSE, sizeof(guint8)); for (int i = 0; i < aid_len; i++) g_array_append_val(aid_data, aid[i]); QmiMessageUimOpenLogicalChannelInput *input; input = qmi_message_uim_open_logical_channel_input_new(); qmi_message_uim_open_logical_channel_input_set_slot(input, qmi_priv->uimSlot, NULL); qmi_message_uim_open_logical_channel_input_set_aid(input, aid_data, NULL); QmiMessageUimOpenLogicalChannelOutput *output; output = qmi_client_uim_open_logical_channel_sync(qmi_priv->uimClient, input, qmi_priv->context, &error); qmi_message_uim_open_logical_channel_input_unref(input); g_array_unref(aid_data); if (!output) { fprintf(stderr, "error: send Open Logical Channel command failed: %s\n", error->message); return -1; } if (!qmi_message_uim_open_logical_channel_output_get_result(output, &error)) { fprintf(stderr, "error: open logical channel operation failed: %s\n", error->message); return -1; } if (!qmi_message_uim_open_logical_channel_output_get_channel_id(output, &channel_id, &error)) { fprintf(stderr, "error: get channel id operation failed: %s\n", error->message); return -1; } qmi_priv->lastChannelId = channel_id; g_debug("Opened logical channel with id %d", channel_id); qmi_message_uim_open_logical_channel_output_unref(output); return channel_id; } void qmi_apdu_interface_logic_channel_close(struct euicc_ctx *ctx, uint8_t channel) { struct qmi_data *qmi_priv = ctx->apdu.interface->userdata; g_autoptr(GError) error = NULL; QmiMessageUimLogicalChannelInput *input; input = qmi_message_uim_logical_channel_input_new(); qmi_message_uim_logical_channel_input_set_slot(input, qmi_priv->uimSlot, NULL); qmi_message_uim_logical_channel_input_set_channel_id(input, channel, NULL); QmiMessageUimLogicalChannelOutput *output; output = qmi_client_uim_logical_channel_sync(qmi_priv->uimClient, input, qmi_priv->context, &error); qmi_message_uim_logical_channel_input_unref(input); if (error) { fprintf(stderr, "error: send Close Logical Channel command failed: %s\n", error->message); return; } if (!qmi_message_uim_logical_channel_output_get_result(output, &error)) { fprintf(stderr, "error: logical channel operation failed: %s\n", error->message); return; } /* Mark channel as having been cleaned up */ if (channel == qmi_priv->lastChannelId) qmi_priv->lastChannelId = -1; g_debug("Closed logical channel with id %d", channel); qmi_message_uim_logical_channel_output_unref(output); } void qmi_apdu_interface_disconnect(struct euicc_ctx *ctx) { struct qmi_data *qmi_priv = ctx->apdu.interface->userdata; g_autoptr(GError) error = NULL; QmiClient *client = QMI_CLIENT(qmi_priv->uimClient); QmiDevice *device = QMI_DEVICE(qmi_client_get_device(client)); qmi_device_release_client_sync(device, client, qmi_priv->context, &error); qmi_priv->uimClient = NULL; g_main_context_unref(qmi_priv->context); qmi_priv->context = NULL; } void qmi_cleanup(struct qmi_data *qmi_priv) { if (qmi_priv->lastChannelId > 0) { fprintf(stderr, "Cleaning up leaked APDU channel %d\n", qmi_priv->lastChannelId); qmi_apdu_interface_logic_channel_close(NULL, qmi_priv->lastChannelId); qmi_priv->lastChannelId = -1; } } estkme-group-lpac-c2fcf5e/driver/apdu/qmi_common.h000066400000000000000000000013711504765665400224070ustar00rootroot00000000000000// SPDX-License-Identifier: MIT /* * Copyright (c) 2024, Luca Weiss */ #pragma once #include "qmi_helpers.h" #include #include struct qmi_data { int lastChannelId; int uimSlot; GMainContext *context; QmiClientUim *uimClient; }; int qmi_apdu_interface_transmit(struct euicc_ctx *ctx, uint8_t **rx, uint32_t *rx_len, const uint8_t *tx, uint32_t tx_len); int qmi_apdu_interface_logic_channel_open(struct euicc_ctx *ctx, const uint8_t *aid, uint8_t aid_len); void qmi_apdu_interface_logic_channel_close(struct euicc_ctx *ctx, uint8_t channel); void qmi_apdu_interface_disconnect(struct euicc_ctx *ctx); void qmi_cleanup(struct qmi_data *qmi_priv); estkme-group-lpac-c2fcf5e/driver/apdu/qmi_helpers.c000066400000000000000000000157101504765665400225560ustar00rootroot00000000000000// SPDX-License-Identifier: MIT /* * Copyright (c) 2024, Luca Weiss */ #include "qmi_helpers.h" static void async_result_ready(GObject *source_object, GAsyncResult *res, gpointer user_data) { GAsyncResult **result_out = user_data; g_assert(*result_out == NULL); *result_out = g_object_ref(res); } #ifdef LPAC_WITH_APDU_QMI_QRTR QrtrBus *qrtr_bus_new_sync(GMainContext *context, GError **error) { g_autoptr(GMainContextPusher) pusher = NULL; g_autoptr(GAsyncResult) result = NULL; pusher = g_main_context_pusher_new(context); qrtr_bus_new(1000, /* ms */ NULL, async_result_ready, &result); while (result == NULL) g_main_context_iteration(context, TRUE); return qrtr_bus_new_finish(result, error); } QmiDevice *qmi_device_new_from_node_sync(QrtrNode *node, GMainContext *context, GError **error) { g_autoptr(GMainContextPusher) pusher = NULL; g_autoptr(GAsyncResult) result = NULL; pusher = g_main_context_pusher_new(context); qmi_device_new_from_node(node, NULL, async_result_ready, &result); while (result == NULL) g_main_context_iteration(context, TRUE); return qmi_device_new_from_node_finish(result, error); } #endif #ifdef LPAC_WITH_APDU_QMI QmiDevice *qmi_device_new_from_path(GFile *file, GMainContext *context, GError **error) { g_autoptr(GMainContextPusher) pusher = NULL; g_autoptr(GAsyncResult) result = NULL; g_autofree gchar *id = NULL; pusher = g_main_context_pusher_new(context); id = g_file_get_path(file); if (id) qmi_device_new(file, NULL, async_result_ready, &result); while (result == NULL) g_main_context_iteration(context, TRUE); return qmi_device_new_finish(result, error); } #endif gboolean qmi_device_open_sync(QmiDevice *device, QmiDeviceOpenFlags flags, GMainContext *context, GError **error) { g_autoptr(GMainContextPusher) pusher = NULL; g_autoptr(GAsyncResult) result = NULL; pusher = g_main_context_pusher_new(context); qmi_device_open(device, flags, 15, NULL, async_result_ready, &result); while (result == NULL) g_main_context_iteration(context, TRUE); return qmi_device_open_finish(device, result, error); } QmiClient *qmi_device_allocate_client_sync(QmiDevice *device, GMainContext *context, GError **error) { g_autoptr(GMainContextPusher) pusher = NULL; g_autoptr(GAsyncResult) result = NULL; pusher = g_main_context_pusher_new(context); qmi_device_allocate_client(device, QMI_SERVICE_UIM, QMI_CID_NONE, 10, NULL, async_result_ready, &result); while (result == NULL) g_main_context_iteration(context, TRUE); return qmi_device_allocate_client_finish(device, result, error); } gboolean qmi_device_release_client_sync(QmiDevice *device, QmiClient *client, GMainContext *context, GError **error) { g_autoptr(GMainContextPusher) pusher = NULL; g_autoptr(GAsyncResult) result = NULL; pusher = g_main_context_pusher_new(context); qmi_device_release_client(device, client, QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID, 10, NULL, async_result_ready, &result); while (result == NULL) g_main_context_iteration(context, TRUE); return qmi_device_release_client_finish(device, result, error); } QmiMessageUimOpenLogicalChannelOutput * qmi_client_uim_open_logical_channel_sync(QmiClientUim *client, QmiMessageUimOpenLogicalChannelInput *input, GMainContext *context, GError **error) { g_autoptr(GMainContextPusher) pusher = NULL; g_autoptr(GAsyncResult) result = NULL; pusher = g_main_context_pusher_new(context); qmi_client_uim_open_logical_channel(client, input, 10, NULL, async_result_ready, &result); while (result == NULL) g_main_context_iteration(context, TRUE); return qmi_client_uim_open_logical_channel_finish(client, result, error); } QmiMessageUimLogicalChannelOutput *qmi_client_uim_logical_channel_sync(QmiClientUim *client, QmiMessageUimLogicalChannelInput *input, GMainContext *context, GError **error) { g_autoptr(GMainContextPusher) pusher = NULL; g_autoptr(GAsyncResult) result = NULL; pusher = g_main_context_pusher_new(context); qmi_client_uim_logical_channel(client, input, 10, NULL, async_result_ready, &result); while (result == NULL) g_main_context_iteration(context, TRUE); return qmi_client_uim_logical_channel_finish(client, result, error); } QmiMessageUimSendApduOutput *qmi_client_uim_send_apdu_sync(QmiClientUim *client, QmiMessageUimSendApduInput *input, GMainContext *context, GError **error) { g_autoptr(GMainContextPusher) pusher = NULL; g_autoptr(GAsyncResult) result = NULL; pusher = g_main_context_pusher_new(context); qmi_client_uim_send_apdu(client, input, 10, NULL, async_result_ready, &result); while (result == NULL) g_main_context_iteration(context, TRUE); return qmi_client_uim_send_apdu_finish(client, result, error); } QmiMessageUimGetSlotStatusOutput *qmi_client_uim_get_slot_status_sync(QmiClientUim *client, GMainContext *context, GError **error) { g_autoptr(GMainContextPusher) pusher = NULL; g_autoptr(GAsyncResult) result = NULL; pusher = g_main_context_pusher_new(context); qmi_client_uim_get_slot_status(client, NULL, 10, NULL, async_result_ready, &result); while (result == NULL) g_main_context_iteration(context, TRUE); return qmi_client_uim_get_slot_status_finish(client, result, error); } QmiMessageUimSwitchSlotOutput *qmi_client_uim_switch_slot_sync(QmiClientUim *client, QmiMessageUimSwitchSlotInput *input, GMainContext *context, GError **error) { g_autoptr(GMainContextPusher) pusher = NULL; g_autoptr(GAsyncResult) result = NULL; pusher = g_main_context_pusher_new(context); qmi_client_uim_switch_slot(client, input, 10, NULL, async_result_ready, &result); while (result == NULL) g_main_context_iteration(context, TRUE); return qmi_client_uim_switch_slot_finish(client, result, error); } QmiMessageUimGetCardStatusOutput *qmi_client_uim_get_card_status_sync(QmiClientUim *client, GMainContext *context, GError **error) { g_autoptr(GMainContextPusher) pusher = NULL; g_autoptr(GAsyncResult) result = NULL; pusher = g_main_context_pusher_new(context); qmi_client_uim_get_card_status(client, NULL, 10, NULL, async_result_ready, &result); while (result == NULL) g_main_context_iteration(context, TRUE); return qmi_client_uim_get_card_status_finish(client, result, error); } estkme-group-lpac-c2fcf5e/driver/apdu/qmi_helpers.h000066400000000000000000000045571504765665400225720ustar00rootroot00000000000000// SPDX-License-Identifier: MIT /* * Copyright (c) 2024, Luca Weiss */ #pragma once #include #include #define ENV_UIM_SLOT APDU_ENV_NAME(QMI, UIM_SLOT) #define ENV_DEVICE APDU_ENV_NAME(QMI, DEVICE) #ifdef LPAC_WITH_APDU_QMI_QRTR # include QrtrBus *qrtr_bus_new_sync(GMainContext *context, GError **error); QmiDevice *qmi_device_new_from_node_sync(QrtrNode *node, GMainContext *context, GError **error); #endif #ifdef LPAC_WITH_APDU_QMI QmiDevice *qmi_device_new_from_path(GFile *file, GMainContext *context, GError **error); #endif gboolean qmi_device_open_sync(QmiDevice *device, QmiDeviceOpenFlags flags, GMainContext *context, GError **error); QmiClient *qmi_device_allocate_client_sync(QmiDevice *device, GMainContext *context, GError **error); gboolean qmi_device_release_client_sync(QmiDevice *device, QmiClient *client, GMainContext *context, GError **error); QmiMessageUimOpenLogicalChannelOutput * qmi_client_uim_open_logical_channel_sync(QmiClientUim *client, QmiMessageUimOpenLogicalChannelInput *input, GMainContext *context, GError **error); QmiMessageUimLogicalChannelOutput *qmi_client_uim_logical_channel_sync(QmiClientUim *client, QmiMessageUimLogicalChannelInput *input, GMainContext *context, GError **error); QmiMessageUimSendApduOutput *qmi_client_uim_send_apdu_sync(QmiClientUim *client, QmiMessageUimSendApduInput *input, GMainContext *context, GError **error); QmiMessageUimGetSlotStatusOutput *qmi_client_uim_get_slot_status_sync(QmiClientUim *client, GMainContext *context, GError **error); QmiMessageUimSwitchSlotOutput *qmi_client_uim_switch_slot_sync(QmiClientUim *client, QmiMessageUimSwitchSlotInput *input, GMainContext *context, GError **error); QmiMessageUimGetCardStatusOutput *qmi_client_uim_get_card_status_sync(QmiClientUim *client, GMainContext *context, GError **error); estkme-group-lpac-c2fcf5e/driver/apdu/qmi_qrtr.c000066400000000000000000000065061504765665400221070ustar00rootroot00000000000000// SPDX-License-Identifier: MIT /* * Copyright (c) 2024, Luca Weiss */ #include "qmi_qrtr.h" #include "qmi_common.h" #include #include #include #include #include #include #include #include static QrtrBus *bus = NULL; static int apdu_interface_connect(struct euicc_ctx *ctx) { struct qmi_data *qmi_priv = ctx->apdu.interface->userdata; g_autoptr(GError) error = NULL; QrtrNode *node = NULL; QmiDevice *device = NULL; QmiClient *client = NULL; bool found = false; qmi_priv->context = g_main_context_new(); bus = qrtr_bus_new_sync(qmi_priv->context, &error); if (bus == NULL) { fprintf(stderr, "error: connect to QRTR bus failed: %s\n", error->message); return -1; } /* Find QRTR node for UIM service */ for (GList *l = qrtr_bus_peek_nodes(bus); l != NULL; l = l->next) { node = l->data; if (node && qrtr_node_lookup_port(node, QMI_SERVICE_UIM) >= 0) { found = true; break; } } if (!found) { fprintf(stderr, "error: find QRTR node with UIM service failed\n"); return -1; } device = qmi_device_new_from_node_sync(node, qmi_priv->context, &error); if (!device) { fprintf(stderr, "error: create QMI device from QRTR node failed: %s\n", error->message); return -1; } qmi_device_open_sync(device, QMI_DEVICE_OPEN_FLAGS_NONE, qmi_priv->context, &error); if (error) { fprintf(stderr, "error: open QMI device failed: %s\n", error->message); return -1; } client = qmi_device_allocate_client_sync(device, qmi_priv->context, &error); if (!client) { fprintf(stderr, "error: allocate QMI client failed: %s\n", error->message); return -1; } qmi_priv->uimClient = QMI_CLIENT_UIM(client); return 0; } static int libapduinterface_init(struct euicc_apdu_interface *ifstruct) { set_deprecated_env_name(ENV_UIM_SLOT, "UIM_SLOT"); struct qmi_data *qmi_priv; qmi_priv = malloc(sizeof(struct qmi_data)); if (!qmi_priv) { fprintf(stderr, "Failed allocating memory\n"); return -1; } memset(ifstruct, 0, sizeof(struct euicc_apdu_interface)); ifstruct->connect = apdu_interface_connect; ifstruct->disconnect = qmi_apdu_interface_disconnect; ifstruct->logic_channel_open = qmi_apdu_interface_logic_channel_open; ifstruct->logic_channel_close = qmi_apdu_interface_logic_channel_close; ifstruct->transmit = qmi_apdu_interface_transmit; /* * Allow the user to select the SIM card slot via environment variable. * Use the primary SIM slot if not set. */ qmi_priv->uimSlot = getenv_or_default(ENV_UIM_SLOT, (int)1); ifstruct->userdata = qmi_priv; return 0; } static int libapduinterface_main(int argc, char **argv) { return 0; } static void libapduinterface_fini(struct euicc_apdu_interface *ifstruct) { struct qmi_data *qmi_priv = ifstruct->userdata; qmi_cleanup(qmi_priv); free(qmi_priv); } const struct euicc_driver driver_apdu_qmi_qrtr = { .type = DRIVER_APDU, .name = "qmi_qrtr", .init = (int (*)(void *))libapduinterface_init, .main = libapduinterface_main, .fini = (void (*)(void *))libapduinterface_fini, }; estkme-group-lpac-c2fcf5e/driver/apdu/qmi_qrtr.h000066400000000000000000000003061504765665400221040ustar00rootroot00000000000000// SPDX-License-Identifier: MIT /* * Copyright (c) 2024, Luca Weiss */ #pragma once #include extern const struct euicc_driver driver_apdu_qmi_qrtr; estkme-group-lpac-c2fcf5e/driver/apdu/stdio.c000066400000000000000000000142631504765665400213720ustar00rootroot00000000000000#include "stdio.h" #include #include #include #include #include #include #include #include #include // getline is a GNU extension, Mingw32 macOS and FreeBSD don't have (a working) one static int afgets(char **obuf, FILE *fp) { uint32_t len = 0; char buffer[2]; char *obuf_new = NULL; *obuf = malloc(1); if ((*obuf) == NULL) { goto err; } (*obuf)[0] = '\0'; while (fgets(buffer, sizeof(buffer), fp) != NULL) { uint32_t fgets_len = strlen(buffer); len += fgets_len + 1; obuf_new = realloc(*obuf, len); if (obuf_new == NULL) { goto err; } *obuf = obuf_new; strcat(*obuf, buffer); if (buffer[fgets_len - 1] == '\n') { break; } } (*obuf)[strcspn(*obuf, "\n")] = 0; return 0; err: free(*obuf); *obuf = NULL; return -1; } static bool json_request(const char *func, const uint8_t *param, unsigned param_len) { _cleanup_free_ char *param_hex = NULL; _cleanup_cjson_ cJSON *jpayload = NULL; if (param && param_len) { param_hex = malloc((2 * param_len) + 1); if (param_hex == NULL) { return false; } if (euicc_hexutil_bin2hex(param_hex, (2 * param_len) + 1, param, param_len) < 0) { return false; } } else { param_hex = NULL; } jpayload = cJSON_CreateObject(); if (jpayload == NULL) { return false; } if (cJSON_AddStringOrNullToObject(jpayload, "func", func) == NULL) { return false; } if (cJSON_AddStringOrNullToObject(jpayload, "param", param_hex) == NULL) { return false; } return json_print("apdu", jpayload); } static int json_response(int *ecode, uint8_t **data, uint32_t *data_len) { int fret = 0; _cleanup_free_ char *data_json; _cleanup_cjson_ cJSON *data_jroot; cJSON *data_payload; cJSON *jtmp; if (data) { *data = NULL; } if (afgets(&data_json, stdin) < 0) { return -1; } data_jroot = cJSON_Parse(data_json); if (data_jroot == NULL) { return -1; } jtmp = cJSON_GetObjectItem(data_jroot, "type"); if (!jtmp) { goto err; } if (!cJSON_IsString(jtmp)) { goto err; } if (strcmp("apdu", jtmp->valuestring) != 0) { goto err; } data_payload = cJSON_GetObjectItem(data_jroot, "payload"); if (!data_payload) { goto err; } if (!cJSON_IsObject(data_payload)) { goto err; } jtmp = cJSON_GetObjectItem(data_payload, "ecode"); if (!jtmp) { goto err; } if (!cJSON_IsNumber(jtmp)) { goto err; } *ecode = jtmp->valueint; jtmp = cJSON_GetObjectItem(data_payload, "data"); if (jtmp && cJSON_IsString(jtmp) && data && data_len) { *data_len = strlen(jtmp->valuestring) / 2; *data = malloc(*data_len); if (!*data) { goto err; } if (euicc_hexutil_hex2bin_r(*data, *data_len, jtmp->valuestring, strlen(jtmp->valuestring)) < 0) { goto err; } } fret = 0; goto exit; err: fret = -1; free(*data); if (data) { *data = NULL; } if (data_len) { *data_len = 0; } *ecode = -1; exit: return fret; } // {"type":"apdu","payload":{"ecode":0}} static int apdu_interface_connect(struct euicc_ctx *ctx) { int ecode; if (json_request("connect", NULL, 0)) { return -1; } if (json_response(&ecode, NULL, NULL)) { return -1; } return ecode; } // {"type":"apdu","payload":{"ecode":0}} static void apdu_interface_disconnect(struct euicc_ctx *ctx) { int ecode; json_request("disconnect", NULL, 0); json_response(&ecode, NULL, NULL); } // {"type":"apdu","payload":{"ecode":1}} static int apdu_interface_logic_channel_open(struct euicc_ctx *ctx, const uint8_t *aid, uint8_t aid_len) { int ecode; if (json_request("logic_channel_open", aid, aid_len)) { return -1; } if (json_response(&ecode, NULL, NULL)) { return -1; } return ecode; } // {"type":"apdu","payload":{"ecode":0}} static void apdu_interface_logic_channel_close(struct euicc_ctx *ctx, uint8_t channel) { int ecode; json_request("logic_channel_close", &channel, sizeof(channel)); json_response(&ecode, NULL, NULL); } // {"type":"apdu","payload":{"ecode":0,"data":"BF3E125A10890490320010012345000123456789019000"}} // {"type":"apdu","payload":{"ecode":0,"data":"BF3C17811574657374726F6F74736D64732E67736D612E636F6D9000"}} // {"type":"apdu","payload":{"ecode":0,"data":"BF2281C6810302010082030202008303040600840F8101008204000628248304000019228504067F36C08603090200870302030088020490A916041481370F5125D0B1D408D4C3B232E6D25E795BEBFBAA16041481370F5125D0B1D408D4C3B232E6D25E795BEBFB990206C004030000010C0D47492D42412D55502D30343139AC48801F312E322E3834302E313233343536372F6D79506C6174666F726D4C6162656C812568747470733A2F2F6D79636F6D70616E792E636F6D2F6D79444C4F415265676973747261729000"}} static int apdu_interface_transmit(struct euicc_ctx *ctx, uint8_t **rx, uint32_t *rx_len, const uint8_t *tx, uint32_t tx_len) { int ecode; if (json_request("transmit", tx, tx_len)) { return -1; } if (json_response(&ecode, rx, rx_len)) { return -1; } return ecode; } static int libapduinterface_init(struct euicc_apdu_interface *ifstruct) { ifstruct->connect = apdu_interface_connect; ifstruct->disconnect = apdu_interface_disconnect; ifstruct->logic_channel_open = apdu_interface_logic_channel_open; ifstruct->logic_channel_close = apdu_interface_logic_channel_close; ifstruct->transmit = apdu_interface_transmit; return 0; } static int libapduinterface_main(int argc, char **argv) { return 0; } static void libapduinterface_fini(struct euicc_apdu_interface *ifstruct) {} const struct euicc_driver driver_apdu_stdio = { .type = DRIVER_APDU, .name = "stdio", .init = (int (*)(void *))libapduinterface_init, .main = libapduinterface_main, .fini = (void (*)(void *))libapduinterface_fini, }; estkme-group-lpac-c2fcf5e/driver/apdu/stdio.h000066400000000000000000000001371504765665400213720ustar00rootroot00000000000000#pragma once #include extern const struct euicc_driver driver_apdu_stdio; estkme-group-lpac-c2fcf5e/driver/driver.c000066400000000000000000000100041504765665400205770ustar00rootroot00000000000000#include "driver.h" #include "driver.private.h" #include #include #include #ifdef LPAC_WITH_APDU_GBINDER # include "driver/apdu/gbinder_hidl.h" #endif #ifdef LPAC_WITH_APDU_MBIM # include "driver/apdu/mbim.h" #endif #ifdef LPAC_WITH_APDU_QMI # include "driver/apdu/qmi.h" #endif #ifdef LPAC_WITH_APDU_QMI_QRTR # include "driver/apdu/qmi_qrtr.h" #endif #ifdef LPAC_WITH_APDU_PCSC # include "driver/apdu/pcsc.h" #endif #ifdef LPAC_WITH_APDU_AT # include "driver/apdu/at.h" #endif #ifdef LPAC_WITH_HTTP_CURL # include "driver/http/curl.h" #endif #ifdef LPAC_WITH_APDU_AT_WIN32 # include "driver/apdu/at_win32.h" #endif #include "driver/apdu/stdio.h" #include "driver/http/stdio.h" static const struct euicc_driver *drivers[] = { #ifdef LPAC_WITH_APDU_GBINDER &driver_apdu_gbinder_hidl, #endif #ifdef LPAC_WITH_APDU_MBIM &driver_apdu_mbim, #endif #ifdef LPAC_WITH_APDU_QMI &driver_apdu_qmi, #endif #ifdef LPAC_WITH_APDU_QMI_QRTR &driver_apdu_qmi_qrtr, #endif #ifdef LPAC_WITH_APDU_PCSC &driver_apdu_pcsc, #endif #ifdef LPAC_WITH_APDU_AT &driver_apdu_at, #endif #ifdef LPAC_WITH_APDU_AT_WIN32 &driver_apdu_at_win32, #endif #ifdef LPAC_WITH_HTTP_CURL &driver_http_curl, #endif &driver_apdu_stdio, &driver_http_stdio, NULL, }; static const struct euicc_driver *_driver_apdu = NULL; static const struct euicc_driver *_driver_http = NULL; struct euicc_apdu_interface euicc_driver_interface_apdu; struct euicc_http_interface euicc_driver_interface_http; int (*euicc_driver_main_apdu)(int argc, char **argv) = NULL; int (*euicc_driver_main_http)(int argc, char **argv) = NULL; static const struct euicc_driver *find_driver(const enum euicc_driver_type type, const char *name) { for (int i = 0; drivers[i] != NULL; i++) { const struct euicc_driver *d = drivers[i]; if (d->type != type) { continue; } if (name == NULL) { return d; } if (strcmp(d->name, name) == 0) { return d; } } return NULL; } int euicc_driver_list(int argc, char **argv) { cJSON *payload = cJSON_CreateObject(); if (payload == NULL) return -1; const struct euicc_driver *driver = NULL; cJSON *driver_name = NULL; cJSON *apdu_drivers = cJSON_CreateArray(); cJSON *http_drivers = cJSON_CreateArray(); if (apdu_drivers == NULL) return -1; for (int i = 0; drivers[i] != NULL; i++) { driver = drivers[i]; driver_name = cJSON_CreateString(driver->name); if (driver_name == NULL) return -1; if (driver->type == DRIVER_APDU) { cJSON_AddItemToArray(apdu_drivers, driver_name); } else if (driver->type == DRIVER_HTTP) { cJSON_AddItemToArray(http_drivers, driver_name); } } cJSON_AddItemToObject(payload, "LPAC_APDU", apdu_drivers); cJSON_AddItemToObject(payload, "LPAC_HTTP", http_drivers); json_print("driver", payload); return 0; } int euicc_driver_init(const char *apdu_driver_name, const char *http_driver_name) { _driver_apdu = find_driver(DRIVER_APDU, apdu_driver_name); if (_driver_apdu == NULL) { fprintf(stderr, "No APDU driver found\n"); return -1; } _driver_http = find_driver(DRIVER_HTTP, http_driver_name); if (_driver_http == NULL) { fprintf(stderr, "No HTTP driver found\n"); return -1; } if (_driver_apdu->init(&euicc_driver_interface_apdu)) { fprintf(stderr, "APDU driver init failed\n"); return -1; } if (_driver_http->init(&euicc_driver_interface_http)) { fprintf(stderr, "HTTP driver init failed\n"); return -1; } euicc_driver_main_apdu = _driver_apdu->main; euicc_driver_main_http = _driver_http->main; return 0; } void euicc_driver_fini() { if (_driver_apdu != NULL) { _driver_apdu->fini(&euicc_driver_interface_apdu); } if (_driver_http != NULL) { _driver_http->fini(&euicc_driver_interface_http); } } estkme-group-lpac-c2fcf5e/driver/driver.h000066400000000000000000000007611504765665400206150ustar00rootroot00000000000000#pragma once #include #include #include extern struct euicc_apdu_interface euicc_driver_interface_apdu; extern struct euicc_http_interface euicc_driver_interface_http; extern int (*euicc_driver_main_apdu)(int argc, char **argv); extern int (*euicc_driver_main_http)(int argc, char **argv); int euicc_driver_list(int argc, char **argv); int euicc_driver_init(const char *apdu_driver_name, const char *http_driver_name); void euicc_driver_fini(void); estkme-group-lpac-c2fcf5e/driver/driver.private.h000066400000000000000000000004121504765665400222570ustar00rootroot00000000000000#pragma once enum euicc_driver_type { DRIVER_APDU, DRIVER_HTTP, }; struct euicc_driver { enum euicc_driver_type type; const char *name; int (*init)(void *interface); int (*main)(int argc, char **argv); void (*fini)(void *interface); }; estkme-group-lpac-c2fcf5e/driver/http/000077500000000000000000000000001504765665400201245ustar00rootroot00000000000000estkme-group-lpac-c2fcf5e/driver/http/curl.c000066400000000000000000000136651504765665400212500ustar00rootroot00000000000000#include "curl.h" #include #include #include #include #include #ifndef _WIN32 # include #else # include # define CURL_GLOBAL_DEFAULT ((1 << 0) | (1 << 1)) # define CURLE_OK 0 # define CURLOPT_URL 10002 # define CURLOPT_WRITEFUNCTION 20011 # define CURLOPT_WRITEDATA 10001 # define CURLOPT_SSL_VERIFYPEER 64 # define CURLOPT_SSL_VERIFYHOST 81 # define CURLOPT_HTTPHEADER 10023 # define CURLOPT_POSTFIELDS 10015 # define CURLOPT_POSTFIELDSIZE 60 # define CURLINFO_RESPONSE_CODE 2097154 typedef void CURL; typedef int CURLcode; typedef int CURLoption; typedef int CURLINFO; static void *libcurl_interface_dlhandle = NULL; #endif struct http_trans_response_data { uint8_t *data; size_t size; }; static struct libcurl_interface { CURLcode (*_curl_global_init)(long flags); CURL *(*_curl_easy_init)(void); CURLcode (*_curl_easy_setopt)(CURL *curl, CURLoption option, ...); CURLcode (*_curl_easy_perform)(CURL *curl); CURLcode (*_curl_easy_getinfo)(CURL *curl, CURLINFO info, ...); const char *(*_curl_easy_strerror)(CURLcode); void (*_curl_easy_cleanup)(CURL *curl); struct curl_slist *(*_curl_slist_append)(struct curl_slist *list, const char *data); void (*_curl_slist_free_all)(struct curl_slist *list); } libcurl; static size_t http_trans_write_callback(void *contents, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; struct http_trans_response_data *mem = (struct http_trans_response_data *)userp; mem->data = realloc(mem->data, mem->size + realsize + 1); if (mem->data == NULL) { /* out of memory! */ printf("not enough memory (realloc returned NULL)\n"); return 0; } memcpy(&(mem->data[mem->size]), contents, realsize); mem->size += realsize; mem->data[mem->size] = 0; return realsize; } static int http_interface_transmit(struct euicc_ctx *ctx, const char *url, uint32_t *rcode, uint8_t **rx, uint32_t *rx_len, const uint8_t *tx, uint32_t tx_len, const char **h) { int fret = 0; CURL *curl; CURLcode res; struct http_trans_response_data responseData = {0}; struct curl_slist *headers = NULL, *nheaders = NULL; long response_code; (*rx) = NULL; (*rcode) = 0; curl = libcurl._curl_easy_init(); if (!curl) { goto err; } libcurl._curl_easy_setopt(curl, CURLOPT_URL, url); libcurl._curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, http_trans_write_callback); libcurl._curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&responseData); libcurl._curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); libcurl._curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); for (int i = 0; h[i] != NULL; i++) { nheaders = libcurl._curl_slist_append(headers, h[i]); if (nheaders == NULL) { goto err; } headers = nheaders; } libcurl._curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); if (tx != NULL) { libcurl._curl_easy_setopt(curl, CURLOPT_POSTFIELDS, tx); libcurl._curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, tx_len); } res = libcurl._curl_easy_perform(curl); if (res != CURLE_OK) { fprintf(stderr, "curl_easy_perform() failed: %s\n", libcurl._curl_easy_strerror(res)); goto err; } libcurl._curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); *rcode = response_code; *rx = responseData.data; *rx_len = responseData.size; fret = 0; goto exit; err: fret = -1; free(responseData.data); exit: libcurl._curl_easy_cleanup(curl); libcurl._curl_slist_free_all(headers); return fret; } static int _init_libcurl(void) { #ifdef _WIN32 if (!(libcurl_interface_dlhandle = dlopen("libcurl.dll", RTLD_LAZY))) { fprintf(stderr, "libcurl init err: %s\n", dlerror()); return -1; } libcurl._curl_global_init = dlsym(libcurl_interface_dlhandle, "curl_global_init"); libcurl._curl_easy_init = dlsym(libcurl_interface_dlhandle, "curl_easy_init"); libcurl._curl_easy_setopt = dlsym(libcurl_interface_dlhandle, "curl_easy_setopt"); libcurl._curl_easy_perform = dlsym(libcurl_interface_dlhandle, "curl_easy_perform"); libcurl._curl_easy_getinfo = dlsym(libcurl_interface_dlhandle, "curl_easy_getinfo"); libcurl._curl_easy_strerror = dlsym(libcurl_interface_dlhandle, "curl_easy_strerror"); libcurl._curl_easy_cleanup = dlsym(libcurl_interface_dlhandle, "curl_easy_cleanup"); libcurl._curl_slist_append = dlsym(libcurl_interface_dlhandle, "curl_slist_append"); libcurl._curl_slist_free_all = dlsym(libcurl_interface_dlhandle, "curl_slist_free_all"); #else libcurl._curl_global_init = curl_global_init; libcurl._curl_easy_init = curl_easy_init; libcurl._curl_easy_setopt = curl_easy_setopt; libcurl._curl_easy_perform = curl_easy_perform; libcurl._curl_easy_getinfo = curl_easy_getinfo; libcurl._curl_easy_strerror = curl_easy_strerror; libcurl._curl_easy_cleanup = curl_easy_cleanup; libcurl._curl_slist_append = curl_slist_append; libcurl._curl_slist_free_all = curl_slist_free_all; #endif return 0; } static int libhttpinterface_init(struct euicc_http_interface *ifstruct) { memset(ifstruct, 0, sizeof(struct euicc_http_interface)); if (_init_libcurl() != 0) { return -1; } if (libcurl._curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) { return -1; } ifstruct->transmit = http_interface_transmit; return 0; } static int libhttpinterface_main(int argc, char **argv) { return 0; } static void libhttpinterface_fini(struct euicc_http_interface *ifstruct) {} const struct euicc_driver driver_http_curl = { .type = DRIVER_HTTP, .name = "curl", .init = (int (*)(void *))libhttpinterface_init, .main = libhttpinterface_main, .fini = (void (*)(void *))libhttpinterface_fini, }; estkme-group-lpac-c2fcf5e/driver/http/curl.h000066400000000000000000000001361504765665400212420ustar00rootroot00000000000000#pragma once #include extern const struct euicc_driver driver_http_curl; estkme-group-lpac-c2fcf5e/driver/http/stdio.c000066400000000000000000000104661504765665400214210ustar00rootroot00000000000000#include "stdio.h" #include #include #include #include #include #include #include #include #include // getline is a GNU extension, Mingw32 macOS and FreeBSD don't have (a working) one static int afgets(char **obuf, FILE *fp) { uint32_t len = 0; char buffer[2]; char *obuf_new = NULL; *obuf = malloc(1); if ((*obuf) == NULL) { goto err; } (*obuf)[0] = '\0'; while (fgets(buffer, sizeof(buffer), fp) != NULL) { uint32_t fgets_len = strlen(buffer); len += fgets_len + 1; obuf_new = realloc(*obuf, len); if (obuf_new == NULL) { goto err; } *obuf = obuf_new; strcat(*obuf, buffer); if (buffer[fgets_len - 1] == '\n') { break; } } (*obuf)[strcspn(*obuf, "\n")] = 0; return 0; err: free(*obuf); *obuf = NULL; return -1; } static bool json_request(const char *url, const uint8_t *tx, uint32_t tx_len, const char **headers) { _cleanup_free_ char *tx_hex = NULL; _cleanup_cjson_ cJSON *jpayload = NULL; cJSON *jheaders = NULL; tx_hex = malloc((2 * tx_len) + 1); if (tx_hex == NULL) { return false; } if (euicc_hexutil_bin2hex(tx_hex, (2 * tx_len) + 1, tx, tx_len) < 0) { return false; } jpayload = cJSON_CreateObject(); if (jpayload == NULL) { return false; } if (cJSON_AddStringOrNullToObject(jpayload, "url", url) == NULL) { return false; } if (cJSON_AddStringOrNullToObject(jpayload, "tx", tx_hex) == NULL) { return false; } jheaders = cJSON_AddArrayToObject(jpayload, "headers"); if (jheaders == NULL) { return false; } for (int i = 0; headers[i] != NULL; i++) { cJSON *jh = cJSON_CreateString(headers[i]); if (jh == NULL) { return false; } cJSON_AddItemToArray(jheaders, jh); } return json_print("http", jpayload); } // {"type":"http","payload":{"rcode":404,"rx":"333435"}} static int http_interface_transmit(struct euicc_ctx *ctx, const char *url, uint32_t *rcode, uint8_t **rx, uint32_t *rx_len, const uint8_t *tx, uint32_t tx_len, const char **headers) { int fret = 0; _cleanup_free_ char *rx_json; _cleanup_cjson_ cJSON *rx_jroot; cJSON *rx_payload; cJSON *jtmp; *rx = NULL; json_request(url, tx, tx_len, headers); if (afgets(&rx_json, stdin) < 0) { return -1; } rx_jroot = cJSON_Parse(rx_json); if (rx_jroot == NULL) { return -1; } jtmp = cJSON_GetObjectItem(rx_jroot, "type"); if (!jtmp) { goto err; } if (!cJSON_IsString(jtmp)) { goto err; } if (strcmp("http", jtmp->valuestring) != 0) { goto err; } rx_payload = cJSON_GetObjectItem(rx_jroot, "payload"); if (!rx_payload) { goto err; } if (!cJSON_IsObject(rx_payload)) { goto err; } jtmp = cJSON_GetObjectItem(rx_payload, "rcode"); if (!jtmp) { goto err; } if (!cJSON_IsNumber(jtmp)) { goto err; } *rcode = jtmp->valueint; jtmp = cJSON_GetObjectItem(rx_payload, "rx"); if (!jtmp) { goto err; } if (!cJSON_IsString(jtmp)) { goto err; } *rx_len = strlen(jtmp->valuestring) / 2; *rx = malloc(*rx_len); if (!*rx) { goto err; } if (euicc_hexutil_hex2bin_r(*rx, *rx_len, jtmp->valuestring, strlen(jtmp->valuestring)) < 0) { goto err; } fret = 0; goto exit; err: fret = -1; free(*rx); *rx = NULL; *rx_len = 0; *rcode = 500; exit: return fret; } static int libhttpinterface_init(struct euicc_http_interface *ifstruct) { memset(ifstruct, 0, sizeof(struct euicc_http_interface)); ifstruct->transmit = http_interface_transmit; return 0; } static int libhttpinterface_main(int argc, char **argv) { return 0; } static void libhttpinterface_fini(struct euicc_http_interface *ifstruct) {} const struct euicc_driver driver_http_stdio = { .type = DRIVER_HTTP, .name = "stdio", .init = (int (*)(void *))libhttpinterface_init, .main = libhttpinterface_main, .fini = (void (*)(void *))libhttpinterface_fini, }; estkme-group-lpac-c2fcf5e/driver/http/stdio.h000066400000000000000000000001371504765665400214200ustar00rootroot00000000000000#pragma once #include extern const struct euicc_driver driver_http_stdio; estkme-group-lpac-c2fcf5e/driver/libeuicc-drivers.pc.in000066400000000000000000000006041504765665400233310ustar00rootroot00000000000000prefix="@CMAKE_INSTALL_PREFIX@" exec_prefix="${prefix}" libdir="${prefix}/lib" includedir="${prefix}/include" Name: libeuicc-drivers Description: An "official" collection of drivers (backends) and their loader for use with libeuicc Version: @PROJECT_VERSION@ Requires: @LIBEUICC_DRIVERS_REQUIRES@ Cflags: -I${includedir} @LIBEUICC_DRIVERS_EXTRA_CFLAGS@ Libs: -L${libdir} -leuicc-drivers estkme-group-lpac-c2fcf5e/euicc/000077500000000000000000000000001504765665400167425ustar00rootroot00000000000000estkme-group-lpac-c2fcf5e/euicc/CMakeLists.txt000066400000000000000000000026001504765665400215000ustar00rootroot00000000000000option(LPAC_DYNAMIC_LIBEUICC "Build and install libeuicc as a dynamic library" OFF) aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} LIB_EUICC_SRCS) if(LPAC_DYNAMIC_LIBEUICC) add_library(euicc SHARED ${LIB_EUICC_SRCS}) else() add_library(euicc STATIC ${LIB_EUICC_SRCS}) endif() target_link_libraries(euicc cjson-static) target_include_directories(euicc PUBLIC $) if(LPAC_DYNAMIC_LIBEUICC) # Install headers file(GLOB ALL_HEADERS "*.h") foreach(header ${ALL_HEADERS}) if(${header} MATCHES "^.*\.private\.h$") list(REMOVE_ITEM ALL_HEADERS ${header}) endif() endforeach() set_target_properties(euicc PROPERTIES PUBLIC_HEADER "${ALL_HEADERS}") # Only useful on Windows, and will lead to invalid arguments on ld.gold. if(WIN32) set_target_properties(euicc PROPERTIES LINK_FLAGS "-Wl,--export-all-symbols") endif() # Install a pkg-config file configure_file(libeuicc.pc.in libeuicc.pc @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libeuicc.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) # Configure libeuicc.so installation set_target_properties(euicc PROPERTIES SOVERSION ${PROJECT_VERSION_MAJOR}) install(TARGETS euicc LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/euicc) endif() estkme-group-lpac-c2fcf5e/euicc/LICENSE000066400000000000000000000635031504765665400177560ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! estkme-group-lpac-c2fcf5e/euicc/base64.c000066400000000000000000000074711504765665400202030ustar00rootroot00000000000000#include "base64.h" #include static const char basis_64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static const unsigned char pr2six[256] = { 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, 64, 0, 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, 64, 64, 64, 64, 64, 64, 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, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64}; int euicc_base64_decode_len(const char *bufcoded) { int nbytesdecoded; register const unsigned char *bufin; register int nprbytes; bufin = (const unsigned char *)bufcoded; while (pr2six[*(bufin++)] <= 63) ; nprbytes = (bufin - (const unsigned char *)bufcoded) - 1; nbytesdecoded = ((nprbytes + 3) / 4) * 3; return nbytesdecoded + 1; } int euicc_base64_decode(unsigned char *bufplain, const char *bufcoded) { int nbytesdecoded; register const unsigned char *bufin; register unsigned char *bufout; register int nprbytes; bufin = (const unsigned char *)bufcoded; while (pr2six[*(bufin++)] <= 63) ; nprbytes = (bufin - (const unsigned char *)bufcoded) - 1; nbytesdecoded = ((nprbytes + 3) / 4) * 3; bufout = (unsigned char *)bufplain; bufin = (const unsigned char *)bufcoded; while (nprbytes > 4) { *(bufout++) = (unsigned char)(pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4); *(bufout++) = (unsigned char)(pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2); *(bufout++) = (unsigned char)(pr2six[bufin[2]] << 6 | pr2six[bufin[3]]); bufin += 4; nprbytes -= 4; } /* Note: (nprbytes == 1) would be an error, so just ignore that case */ if (nprbytes > 1) { *(bufout++) = (unsigned char)(pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4); } if (nprbytes > 2) { *(bufout++) = (unsigned char)(pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2); } if (nprbytes > 3) { *(bufout++) = (unsigned char)(pr2six[bufin[2]] << 6 | pr2six[bufin[3]]); } *(bufout++) = '\0'; nbytesdecoded -= (4 - nprbytes) & 3; return nbytesdecoded; } int euicc_base64_encode_len(int len) { return ((len + 2) / 3 * 4) + 1; } int euicc_base64_encode(char *encoded, const unsigned char *string, int len) { int i; char *p; p = encoded; for (i = 0; i < len - 2; i += 3) { *p++ = basis_64[(string[i] >> 2) & 0x3F]; *p++ = basis_64[((string[i] & 0x3) << 4) | ((int)(string[i + 1] & 0xF0) >> 4)]; *p++ = basis_64[((string[i + 1] & 0xF) << 2) | ((int)(string[i + 2] & 0xC0) >> 6)]; *p++ = basis_64[string[i + 2] & 0x3F]; } if (i < len) { *p++ = basis_64[(string[i] >> 2) & 0x3F]; if (i == (len - 1)) { *p++ = basis_64[((string[i] & 0x3) << 4)]; *p++ = '='; } else { *p++ = basis_64[((string[i] & 0x3) << 4) | ((int)(string[i + 1] & 0xF0) >> 4)]; *p++ = basis_64[((string[i + 1] & 0xF) << 2)]; } *p++ = '='; } *p++ = '\0'; return p - encoded; } estkme-group-lpac-c2fcf5e/euicc/base64.h000066400000000000000000000003751504765665400202040ustar00rootroot00000000000000#pragma once int euicc_base64_decode_len(const char *bufcoded); int euicc_base64_decode(unsigned char *bufplain, const char *bufcoded); int euicc_base64_encode_len(int len); int euicc_base64_encode(char *encoded, const unsigned char *string, int len); estkme-group-lpac-c2fcf5e/euicc/derutil.c000066400000000000000000000232551504765665400205650ustar00rootroot00000000000000#include "derutil.h" #include #include int euicc_derutil_unpack_first(struct euicc_derutil_node *result, const uint8_t *buffer, uint32_t buffer_len) { const uint8_t *cptr; uint32_t rlen; cptr = buffer; rlen = buffer_len; memset(result, 0x00, sizeof(struct euicc_derutil_node)); if (rlen < 1) { return -1; } result->tag = *cptr; cptr++; rlen--; if ((result->tag & 0x1F) == 0x1F) { if (rlen < 1) { return -1; } result->tag = (result->tag << 8) | *cptr; cptr++; rlen--; } if (rlen < 1) { return -1; } result->length = *cptr; cptr++; rlen--; if (result->length & 0x80) { uint8_t lengthlen = result->length & 0x7F; if (rlen < lengthlen) { return -1; } result->length = 0; for (int i = 0; i < lengthlen; i++) { result->length = (result->length << 8) | *cptr; cptr++; rlen--; } } if (rlen < result->length) { return -1; } result->value = cptr; result->self.ptr = buffer; result->self.length = result->value - result->self.ptr + result->length; return 0; } int euicc_derutil_unpack_next(struct euicc_derutil_node *result, struct euicc_derutil_node *prev, const uint8_t *buffer, uint32_t buffer_len) { const uint8_t *cptr; uint32_t rlen; cptr = prev->self.ptr + prev->self.length; rlen = buffer_len - (cptr - buffer); return euicc_derutil_unpack_first(result, cptr, rlen); } int euicc_derutil_unpack_find_alias_tags(struct euicc_derutil_node *result, const uint16_t *tags, uint32_t tags_count, const uint8_t *buffer, uint32_t buffer_len) { result->self.ptr = buffer; result->self.length = 0; while (euicc_derutil_unpack_next(result, result, buffer, buffer_len) == 0) { for (uint32_t i = 0; i < tags_count; i++) { if (result->tag == tags[i]) { return 0; } } } return -1; } int euicc_derutil_unpack_find_tag(struct euicc_derutil_node *result, uint16_t tag, const uint8_t *buffer, uint32_t buffer_len) { return euicc_derutil_unpack_find_alias_tags(result, &tag, 1, buffer, buffer_len); } static void euicc_derutil_pack_sizeof_single_node(struct euicc_derutil_node *node) { node->self.length = 0; if (node->pack.headless) { node->self.length = node->length; return; } if (node->tag >> 8) { node->self.length += 2; } else { node->self.length += 1; } if (node->length < 0x80) { node->self.length += 1; } else { uint8_t lengthlen = 0; uint32_t length = node->length; while (length) { length >>= 8; lengthlen++; } node->self.length += 1 + lengthlen; } node->self.length += node->length; } static int euicc_derutil_pack_iterate_size_and_relative_offset(struct euicc_derutil_node *node, struct euicc_derutil_node *parent, uint32_t relative_offset) { uint32_t full_size = 0; while (node) { node->pack.relative_offset = relative_offset; if (node->pack.child) { node->length = 0; euicc_derutil_pack_iterate_size_and_relative_offset(node->pack.child, node, relative_offset); } euicc_derutil_pack_sizeof_single_node(node); if (parent) { parent->length += node->self.length; } relative_offset += node->self.length; full_size += node->self.length; node = node->pack.next; } return full_size; } static void euicc_derutil_pack_iterate_ptrs(struct euicc_derutil_node *node, uint8_t *wptr) { while (node) { node->self.ptr = wptr; if (node->pack.child) { euicc_derutil_pack_iterate_ptrs(node->pack.child, (wptr + node->self.length - node->length)); } wptr += node->self.length; node = node->pack.next; } } static void euicc_derutil_pack_copydata_single_node(struct euicc_derutil_node *node) { uint8_t *buffer = (uint8_t *)(node->self.ptr); if (node->pack.headless) { memcpy(buffer, node->value, node->length); return; } if (node->tag >> 8) { *buffer = node->tag >> 8; buffer++; } *buffer = node->tag & 0xFF; buffer++; if (node->length < 0x80) { *buffer = node->length; buffer++; } else { uint8_t lengthlen = 0; uint32_t length = node->length; while (length) { length >>= 8; lengthlen++; } *buffer = 0x80 | lengthlen; buffer++; for (int i = lengthlen - 1; i >= 0; i--) { *buffer = (node->length >> (i * 8)) & 0xFF; buffer++; } } if (node->value && !node->pack.child) { memcpy(buffer, node->value, node->length); } else { node->value = buffer; } } static void euicc_derutil_pack_iterate_copydata(struct euicc_derutil_node *node) { while (node) { euicc_derutil_pack_copydata_single_node(node); if (node->pack.child) { euicc_derutil_pack_iterate_copydata(node->pack.child); } node = node->pack.next; } } static void euicc_derutil_pack_finish(struct euicc_derutil_node *node, uint8_t *buffer) { euicc_derutil_pack_iterate_ptrs(node, buffer); euicc_derutil_pack_iterate_copydata(node); } int euicc_derutil_pack(uint8_t *buffer, uint32_t *buffer_len, struct euicc_derutil_node *node) { uint32_t required_size = 0; required_size = euicc_derutil_pack_iterate_size_and_relative_offset(node, NULL, 0); if (*buffer_len < required_size) { return -1; } euicc_derutil_pack_finish(node, buffer); *buffer_len = required_size; return 0; } int euicc_derutil_pack_alloc(uint8_t **buffer, uint32_t *buffer_len, struct euicc_derutil_node *node) { uint32_t required_size = 0; required_size = euicc_derutil_pack_iterate_size_and_relative_offset(node, NULL, 0); *buffer_len = required_size; *buffer = malloc(*buffer_len); if (!*buffer) { return -1; } euicc_derutil_pack_finish(node, *buffer); return 0; } long euicc_derutil_convert_bin2long(const uint8_t *buffer, uint32_t buffer_len) { long result = 0; for (uint32_t i = 0; i < buffer_len; i++) { result = (result << 8) | buffer[i]; } return result; } int euicc_derutil_convert_long2bin(uint8_t *buffer, uint32_t *buffer_len, long value) { uint8_t required_len = 1; for (int i = 1; i < sizeof(value); i++) { if ((value >> (i * 8))) { required_len++; } else { if (value > 0) { if ((value >> ((i - 1) * 8)) & 0x80) { required_len++; } } break; } } if (required_len > *buffer_len) { return -1; } for (int i = 0; i < required_len; i++) { buffer[i] = (value >> ((required_len - i - 1) * 8)) & 0xFF; } *buffer_len = required_len; return 0; } static uint32_t euicc_derutil_convert_bits2bin_sizeof(const uint32_t *bits, uint32_t bits_count) { uint32_t max_bit = 0; for (uint32_t i = 0; i < bits_count; i++) { if (bits[i] > max_bit) { max_bit = bits[i]; } } return ((max_bit + 8) / 8) + 1; } int euicc_derutil_convert_bits2bin(uint8_t *buffer, uint32_t buffer_len, const uint32_t *bits, uint32_t bits_count) { if (buffer_len < euicc_derutil_convert_bits2bin_sizeof(bits, bits_count)) { return -1; } memset(buffer, 0x00, buffer_len); buffer[0] = 0x00; for (uint32_t i = 0; i < bits_count; i++) { buffer[(bits[i] / 8) + 1] |= 1 << (7 - (bits[i] % 8)); } return 0; } int euicc_derutil_convert_bits2bin_alloc(uint8_t **buffer, uint32_t *buffer_len, const uint32_t *bits, uint32_t bits_count) { *buffer_len = euicc_derutil_convert_bits2bin_sizeof(bits, bits_count); *buffer = malloc(*buffer_len); if (!*buffer) { return -1; } return euicc_derutil_convert_bits2bin(*buffer, *buffer_len, bits, bits_count); } int euicc_derutil_convert_bin2bits_str(const char ***output, const uint8_t *buffer, int buffer_len, const char **desc) { int max_cap_len = 0; int flags_reg; int flags_count = 0; const char **wptr; char unused; *output = NULL; if (buffer_len < 1) { return -1; } unused = *buffer; buffer++; buffer_len--; for (max_cap_len = 0; desc[max_cap_len]; max_cap_len++) ; for (int j = 0; j < buffer_len; j++) { if (j == buffer_len - 1) { flags_reg = buffer[j] & ~(0xFF >> (8 - unused)); } else { flags_reg = buffer[j]; } for (int i = 0; (i < 8) && ((j * 8 + i) < max_cap_len); i++) { if (flags_reg & 0x80) { flags_count++; } flags_reg <<= 1; } } wptr = calloc(flags_count + 1, sizeof(char *)); if (!wptr) { return -1; } *output = wptr; for (int j = 0; j < buffer_len; j++) { if (j == buffer_len - 1) { flags_reg = buffer[j] & ~(0xFF >> (8 - unused)); } else { flags_reg = buffer[j]; } for (int i = 0; (i < 8) && ((j * 8 + i) < max_cap_len); i++) { if (flags_reg & 0x80) { *(wptr++) = desc[j * 8 + i]; } flags_reg <<= 1; } } return 0; } estkme-group-lpac-c2fcf5e/euicc/derutil.h000066400000000000000000000034131504765665400205640ustar00rootroot00000000000000#pragma once #include struct euicc_derutil_node { uint16_t tag; uint32_t length; const uint8_t *value; struct { const uint8_t *ptr; uint32_t length; } self; struct { uint8_t headless; uint32_t relative_offset; struct euicc_derutil_node *child; struct euicc_derutil_node *next; } pack; }; int euicc_derutil_unpack_first(struct euicc_derutil_node *result, const uint8_t *buffer, uint32_t buffer_len); int euicc_derutil_unpack_next(struct euicc_derutil_node *result, struct euicc_derutil_node *prev, const uint8_t *buffer, uint32_t buffer_len); int euicc_derutil_unpack_find_alias_tags(struct euicc_derutil_node *result, const uint16_t *tags, uint32_t tags_count, const uint8_t *buffer, uint32_t buffer_len); int euicc_derutil_unpack_find_tag(struct euicc_derutil_node *result, uint16_t tag, const uint8_t *buffer, uint32_t buffer_len); int euicc_derutil_pack(uint8_t *buffer, uint32_t *buffer_len, struct euicc_derutil_node *node); int euicc_derutil_pack_alloc(uint8_t **buffer, uint32_t *buffer_len, struct euicc_derutil_node *node); long euicc_derutil_convert_bin2long(const uint8_t *buffer, uint32_t buffer_len); int euicc_derutil_convert_long2bin(uint8_t *buffer, uint32_t *buffer_len, long value); int euicc_derutil_convert_bits2bin(uint8_t *buffer, uint32_t buffer_len, const uint32_t *bits, uint32_t bits_count); int euicc_derutil_convert_bits2bin_alloc(uint8_t **buffer, uint32_t *buffer_len, const uint32_t *bits, uint32_t bits_count); int euicc_derutil_convert_bin2bits_str(const char ***output, const uint8_t *buffer, int buffer_len, const char **desc); estkme-group-lpac-c2fcf5e/euicc/es10a.c000066400000000000000000000067631504765665400200330ustar00rootroot00000000000000#include "es10a.h" #include "derutil.h" #include "euicc.private.h" #include "hexutil.h" #include #include #include #include #include int es10a_get_euicc_configured_addresses(struct euicc_ctx *ctx, struct es10a_euicc_configured_addresses *address) { int fret = 0; struct euicc_derutil_node n_request = { .tag = 0xBF3C, // EuiccConfiguredAddressesRequest }; uint32_t reqlen; uint8_t *respbuf = NULL; unsigned resplen; struct euicc_derutil_node tmpnode, n_Response; memset(address, 0, sizeof(*address)); reqlen = sizeof(ctx->apdu._internal.request_buffer.body); if (euicc_derutil_pack(ctx->apdu._internal.request_buffer.body, &reqlen, &n_request)) { goto err; } if (es10x_command(ctx, &respbuf, &resplen, ctx->apdu._internal.request_buffer.body, reqlen) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&n_Response, n_request.tag, respbuf, resplen)) { goto err; } if (euicc_derutil_unpack_find_tag(&tmpnode, 0x80, n_Response.value, n_Response.length) == 0) { address->defaultDpAddress = malloc(tmpnode.length + 1); if (address->defaultDpAddress) { memcpy(address->defaultDpAddress, tmpnode.value, tmpnode.length); address->defaultDpAddress[tmpnode.length] = '\0'; } } if (euicc_derutil_unpack_find_tag(&tmpnode, 0x81, n_Response.value, n_Response.length) == 0) { address->rootDsAddress = malloc(tmpnode.length + 1); if (address->rootDsAddress) { memcpy(address->rootDsAddress, tmpnode.value, tmpnode.length); address->rootDsAddress[tmpnode.length] = '\0'; } } goto exit; err: fret = -1; free(address->defaultDpAddress); address->defaultDpAddress = NULL; free(address->rootDsAddress); address->rootDsAddress = NULL; exit: free(respbuf); respbuf = NULL; return fret; } int es10a_set_default_dp_address(struct euicc_ctx *ctx, const char *smdp) { int fret = 0; struct euicc_derutil_node n_request = { .tag = 0xBF3F, // SetDefaultDpAddressRequest .pack = { .child = &(struct euicc_derutil_node){ .tag = 0x80, .length = strlen(smdp), .value = (const uint8_t *)smdp, }, }, }; uint32_t reqlen; uint8_t *respbuf = NULL; unsigned resplen; struct euicc_derutil_node tmpnode; reqlen = sizeof(ctx->apdu._internal.request_buffer.body); if (euicc_derutil_pack(ctx->apdu._internal.request_buffer.body, &reqlen, &n_request)) { goto err; } if (es10x_command(ctx, &respbuf, &resplen, ctx->apdu._internal.request_buffer.body, reqlen) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&tmpnode, n_request.tag, respbuf, resplen) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&tmpnode, 0x80, tmpnode.value, tmpnode.length) < 0) { goto err; } fret = euicc_derutil_convert_bin2long(tmpnode.value, tmpnode.length); goto exit; err: fret = -1; exit: free(respbuf); respbuf = NULL; return fret; } void es10a_euicc_configured_addresses_free(struct es10a_euicc_configured_addresses *address) { if (!address) { return; } free(address->defaultDpAddress); free(address->rootDsAddress); memset(address, 0x00, sizeof(struct es10a_euicc_configured_addresses)); } estkme-group-lpac-c2fcf5e/euicc/es10a.h000066400000000000000000000006421504765665400200260ustar00rootroot00000000000000#pragma once #include "euicc.h" struct es10a_euicc_configured_addresses { char *defaultDpAddress; char *rootDsAddress; }; int es10a_get_euicc_configured_addresses(struct euicc_ctx *ctx, struct es10a_euicc_configured_addresses *address); int es10a_set_default_dp_address(struct euicc_ctx *ctx, const char *smdp); void es10a_euicc_configured_addresses_free(struct es10a_euicc_configured_addresses *address); estkme-group-lpac-c2fcf5e/euicc/es10b.c000066400000000000000000001267561504765665400200410ustar00rootroot00000000000000#include "es10b.h" #include "euicc.private.h" #include "base64.h" #include "derutil.h" #include "hexutil.h" #include "sha256.h" #include #include #include #include #include int es10b_prepare_download_r(struct euicc_ctx *ctx, char **b64_PrepareDownloadResponse, struct es10b_prepare_download_param *param, struct es10b_prepare_download_param_user *param_user) { int fret = 0; uint8_t *reqbuf = NULL; uint32_t reqlen; uint8_t *respbuf = NULL; unsigned resplen; EUICC_SHA256_CTX sha256ctx; uint8_t hashCC[SHA256_BLOCK_SIZE]; uint8_t *smdpSigned2 = NULL, *smdpSignature2 = NULL, *smdpCertificate = NULL; int smdpSigned2_len, smdpSignature2_len, smdpCertificate_len; struct euicc_derutil_node n_request, n_smdpSigned2, n_smdpSignature2, n_smdpCertificate, n_hashCc, n_transactionId, n_ccRequiredFlag; *b64_PrepareDownloadResponse = NULL; memset(&n_request, 0, sizeof(n_request)); memset(&n_smdpSigned2, 0, sizeof(n_smdpSigned2)); memset(&n_smdpSignature2, 0, sizeof(n_smdpSignature2)); memset(&n_smdpCertificate, 0, sizeof(n_smdpCertificate)); memset(&n_hashCc, 0, sizeof(n_hashCc)); smdpSigned2 = malloc(euicc_base64_decode_len(param->b64_smdpSigned2)); if (!smdpSigned2) { goto err; } smdpSignature2 = malloc(euicc_base64_decode_len(param->b64_smdpSignature2)); if (!smdpSignature2) { goto err; } smdpCertificate = malloc(euicc_base64_decode_len(param->b64_smdpCertificate)); if (!smdpCertificate) { goto err; } if ((smdpSigned2_len = euicc_base64_decode(smdpSigned2, param->b64_smdpSigned2)) < 0) { goto err; } if ((smdpSignature2_len = euicc_base64_decode(smdpSignature2, param->b64_smdpSignature2)) < 0) { goto err; } if ((smdpCertificate_len = euicc_base64_decode(smdpCertificate, param->b64_smdpCertificate)) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&n_smdpSigned2, 0x30, smdpSigned2, smdpSigned2_len) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&n_smdpSignature2, 0x5F37, smdpSignature2, smdpSignature2_len) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&n_smdpCertificate, 0x30, smdpCertificate, smdpCertificate_len) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&n_transactionId, 0x80, n_smdpSigned2.value, n_smdpSigned2.length) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&n_ccRequiredFlag, 0x01, n_smdpSigned2.value, n_smdpSigned2.length) < 0) { goto err; } n_request.tag = 0xBF21; n_request.pack.child = &n_smdpSigned2; n_smdpSigned2.pack.next = &n_smdpSignature2; if (euicc_derutil_convert_bin2long(n_ccRequiredFlag.value, n_ccRequiredFlag.length)) { if ((!param_user->confirmationCode) || (strlen(param_user->confirmationCode) == 0)) { goto err; } memset(&sha256ctx, 0, sizeof(sha256ctx)); euicc_sha256_init(&sha256ctx); euicc_sha256_update(&sha256ctx, (const uint8_t *)param_user->confirmationCode, strlen(param_user->confirmationCode)); euicc_sha256_final(&sha256ctx, hashCC); memset(&sha256ctx, 0, sizeof(sha256ctx)); euicc_sha256_init(&sha256ctx); euicc_sha256_update(&sha256ctx, hashCC, sizeof(hashCC)); euicc_sha256_update(&sha256ctx, n_transactionId.value, n_transactionId.length); euicc_sha256_final(&sha256ctx, hashCC); n_hashCc.tag = 0x04; n_hashCc.value = hashCC; n_hashCc.length = sizeof(hashCC); n_smdpSignature2.pack.next = &n_hashCc; n_hashCc.pack.next = &n_smdpCertificate; } else { n_smdpSignature2.pack.next = &n_smdpCertificate; } if (euicc_derutil_pack_alloc(&reqbuf, &reqlen, &n_request) < 0) { goto err; } free(smdpSigned2); smdpSigned2 = NULL; free(smdpSignature2); smdpSignature2 = NULL; free(smdpCertificate); smdpCertificate = NULL; if (es10x_command(ctx, &respbuf, &resplen, reqbuf, reqlen) < 0) { goto err; } free(reqbuf); reqbuf = NULL; *b64_PrepareDownloadResponse = malloc(euicc_base64_encode_len(resplen)); if (!(*b64_PrepareDownloadResponse)) { goto err; } if (euicc_base64_encode(*b64_PrepareDownloadResponse, respbuf, resplen) < 0) { goto err; } fret = 0; goto exit; err: fret = -1; free(*b64_PrepareDownloadResponse); *b64_PrepareDownloadResponse = NULL; exit: free(smdpSigned2); smdpSigned2 = NULL; free(smdpSignature2); smdpSignature2 = NULL; free(smdpCertificate); smdpCertificate = NULL; free(reqbuf); reqbuf = NULL; free(respbuf); respbuf = NULL; return fret; } static int es10b_load_bound_profile_package_tx(struct euicc_ctx *ctx, struct es10b_load_bound_profile_package_result *result, const uint8_t *reqbuf, int reqbuf_len) { int fret = 0; uint8_t *respbuf = NULL; unsigned resplen; result->seqNumber = 0; result->bppCommandId = ES10B_BPP_COMMAND_ID_UNDEFINED; result->errorReason = ES10B_ERROR_REASON_UNDEFINED; if (es10x_command(ctx, &respbuf, &resplen, reqbuf, reqbuf_len) < 0) { goto err; } if (resplen > 0) { struct euicc_derutil_node tmpnode, n_notificationMetadata, n_sequenceNumber, n_finalResult; if (euicc_derutil_unpack_find_tag(&tmpnode, 0xBF37, respbuf, resplen) < 0) // ProfileInstallationResult { goto err; } if (euicc_derutil_unpack_find_tag(&tmpnode, 0xBF27, tmpnode.value, tmpnode.length) < 0) // ProfileInstallationResultData { goto err; } if (euicc_derutil_unpack_find_tag(&n_notificationMetadata, 0xBF2F, tmpnode.value, tmpnode.length) < 0) // NotificationMetadata { goto err; } if (euicc_derutil_unpack_find_tag(&tmpnode, 0xA2, tmpnode.value, tmpnode.length) < 0) // finalResult { goto err; } if (euicc_derutil_unpack_first(&n_finalResult, tmpnode.value, tmpnode.length) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&n_sequenceNumber, 0x80, n_notificationMetadata.value, n_notificationMetadata.length) == 0) { result->seqNumber = euicc_derutil_convert_bin2long(n_sequenceNumber.value, n_sequenceNumber.length); } switch (n_finalResult.tag) { case 0xA0: // SuccessResult break; case 0xA1: // ErrorResult tmpnode.self.ptr = n_finalResult.value; tmpnode.self.length = 0; while (euicc_derutil_unpack_next(&tmpnode, &tmpnode, n_finalResult.value, n_finalResult.length) == 0) { long tmpint; switch (tmpnode.tag) { case 0x80: tmpint = euicc_derutil_convert_bin2long(tmpnode.value, tmpnode.length); switch (tmpint) { case ES10B_BPP_COMMAND_ID_INITIALISE_SECURE_CHANNEL: case ES10B_BPP_COMMAND_ID_CONFIGURE_ISDP: case ES10B_BPP_COMMAND_ID_STORE_METADATA: case ES10B_BPP_COMMAND_ID_STORE_METADATA2: case ES10B_BPP_COMMAND_ID_REPLACE_SESSION_KEYS: case ES10B_BPP_COMMAND_ID_LOAD_PROFILE_ELEMENTS: result->bppCommandId = tmpint; break; default: result->bppCommandId = ES10B_BPP_COMMAND_ID_UNDEFINED; break; } break; case 0x81: tmpint = euicc_derutil_convert_bin2long(tmpnode.value, tmpnode.length); switch (tmpint) { case ES10B_ERROR_REASON_INCORRECT_INPUT_VALUES: case ES10B_ERROR_REASON_INVALID_SIGNATURE: case ES10B_ERROR_REASON_INVALID_TRANSACTION_ID: case ES10B_ERROR_REASON_UNSUPPORTED_CRT_VALUES: case ES10B_ERROR_REASON_UNSUPPORTED_REMOTE_OPERATION_TYPE: case ES10B_ERROR_REASON_UNSUPPORTED_PROFILE_CLASS: case ES10B_ERROR_REASON_SCP03T_STRUCTURE_ERROR: case ES10B_ERROR_REASON_SCP03T_SECURITY_ERROR: case ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_ICCID_ALREADY_EXISTS_ON_EUICC: case ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_INSUFFICIENT_MEMORY_FOR_PROFILE: case ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_INTERRUPTION: case ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_PE_PROCESSING_ERROR: case ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_DATA_MISMATCH: case ES10B_ERROR_REASON_TEST_PROFILE_INSTALL_FAILED_DUE_TO_INVALID_NAA_KEY: case ES10B_ERROR_REASON_PPR_NOT_ALLOWED: case ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_UNKNOWN_ERROR: result->errorReason = tmpint; break; default: result->errorReason = ES10B_ERROR_REASON_UNDEFINED; break; } break; default: break; } } goto err; default: goto err; } } fret = 0; goto exit; err: fret = -1; exit: free(respbuf); respbuf = NULL; return fret; } int es10b_load_bound_profile_package_r(struct euicc_ctx *ctx, struct es10b_load_bound_profile_package_result *result, const char *b64_BoundProfilePackage) { int fret = 0; uint8_t *bpp = NULL; int bpp_len; const uint8_t *reqbuf; int reqbuf_len; struct euicc_derutil_node tmpnode, tmpchildnode, n_BoundProfilePackage; bpp = malloc(euicc_base64_decode_len(b64_BoundProfilePackage)); if (!bpp) { goto err; } if ((bpp_len = euicc_base64_decode(bpp, b64_BoundProfilePackage)) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&n_BoundProfilePackage, 0xBF36, bpp, bpp_len) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&tmpnode, 0xBF23, n_BoundProfilePackage.value, n_BoundProfilePackage.length) < 0) { goto err; } reqbuf = n_BoundProfilePackage.self.ptr; reqbuf_len = tmpnode.self.ptr - n_BoundProfilePackage.self.ptr + tmpnode.self.length; if (es10b_load_bound_profile_package_tx(ctx, result, reqbuf, reqbuf_len) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&tmpnode, 0xA0, n_BoundProfilePackage.value, n_BoundProfilePackage.length) < 0) { goto err; } reqbuf = tmpnode.self.ptr; reqbuf_len = tmpnode.self.length; if (es10b_load_bound_profile_package_tx(ctx, result, reqbuf, reqbuf_len) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&tmpnode, 0xA1, n_BoundProfilePackage.value, n_BoundProfilePackage.length) < 0) { goto err; } reqbuf = tmpnode.self.ptr; reqbuf_len = tmpnode.value - tmpnode.self.ptr; if (es10b_load_bound_profile_package_tx(ctx, result, reqbuf, reqbuf_len) < 0) { goto err; } tmpchildnode.self.ptr = tmpnode.value; tmpchildnode.self.length = 0; while (euicc_derutil_unpack_next(&tmpchildnode, &tmpchildnode, tmpnode.value, tmpnode.length) == 0) { reqbuf = tmpchildnode.self.ptr; reqbuf_len = tmpchildnode.self.length; if (es10b_load_bound_profile_package_tx(ctx, result, reqbuf, reqbuf_len) < 0) { goto err; } } if (euicc_derutil_unpack_find_tag(&tmpnode, 0xA2, n_BoundProfilePackage.value, n_BoundProfilePackage.length) == 0) { reqbuf = tmpnode.self.ptr; reqbuf_len = tmpnode.self.length; if (es10b_load_bound_profile_package_tx(ctx, result, reqbuf, reqbuf_len) < 0) { goto err; } } if (euicc_derutil_unpack_find_tag(&tmpnode, 0xA3, n_BoundProfilePackage.value, n_BoundProfilePackage.length) < 0) { goto err; } reqbuf = tmpnode.self.ptr; reqbuf_len = tmpnode.value - tmpnode.self.ptr; if (es10b_load_bound_profile_package_tx(ctx, result, reqbuf, reqbuf_len) < 0) { goto err; } tmpchildnode.self.ptr = tmpnode.value; tmpchildnode.self.length = 0; while (euicc_derutil_unpack_next(&tmpchildnode, &tmpchildnode, tmpnode.value, tmpnode.length) == 0) { reqbuf = tmpchildnode.self.ptr; reqbuf_len = tmpchildnode.self.length; if (es10b_load_bound_profile_package_tx(ctx, result, reqbuf, reqbuf_len) < 0) { goto err; } } goto exit; err: fret = -1; exit: free(bpp); bpp = NULL; return fret; } int es10b_get_euicc_challenge_r(struct euicc_ctx *ctx, char **b64_euiccChallenge) { int fret = 0; struct euicc_derutil_node n_request = { .tag = 0xBF2E, // GetEuiccDataRequest }; uint32_t reqlen; uint8_t *respbuf = NULL; unsigned resplen; struct euicc_derutil_node tmpnode; reqlen = sizeof(ctx->apdu._internal.request_buffer.body); if (euicc_derutil_pack(ctx->apdu._internal.request_buffer.body, &reqlen, &n_request)) { goto err; } if (es10x_command(ctx, &respbuf, &resplen, ctx->apdu._internal.request_buffer.body, reqlen) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&tmpnode, n_request.tag, respbuf, resplen)) { goto err; } if (euicc_derutil_unpack_find_tag(&tmpnode, 0x80, tmpnode.value, tmpnode.length)) { goto err; } *b64_euiccChallenge = malloc(euicc_base64_encode_len(tmpnode.length)); if (!(*b64_euiccChallenge)) { goto err; } if (euicc_base64_encode(*b64_euiccChallenge, tmpnode.value, tmpnode.length) < 0) { goto err; } goto exit; err: fret = -1; free(*b64_euiccChallenge); *b64_euiccChallenge = NULL; exit: free(respbuf); respbuf = NULL; return fret; } int es10b_get_euicc_info_r(struct euicc_ctx *ctx, char **b64_EUICCInfo1) { int fret = 0; struct euicc_derutil_node n_request = { .tag = 0xBF20, // GetEuiccInfo1Request }; uint32_t reqlen; uint8_t *respbuf = NULL; unsigned resplen; struct euicc_derutil_node tmpnode; reqlen = sizeof(ctx->apdu._internal.request_buffer.body); if (euicc_derutil_pack(ctx->apdu._internal.request_buffer.body, &reqlen, &n_request)) { goto err; } if (es10x_command(ctx, &respbuf, &resplen, ctx->apdu._internal.request_buffer.body, reqlen) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&tmpnode, n_request.tag, respbuf, resplen)) { goto err; } *b64_EUICCInfo1 = malloc(euicc_base64_encode_len(tmpnode.self.length)); if (!(*b64_EUICCInfo1)) { goto err; } if (euicc_base64_encode(*b64_EUICCInfo1, tmpnode.self.ptr, tmpnode.self.length) < 0) { goto err; } goto exit; err: fret = -1; free(*b64_EUICCInfo1); *b64_EUICCInfo1 = NULL; exit: free(respbuf); respbuf = NULL; return fret; } int es10b_authenticate_server_r(struct euicc_ctx *ctx, uint8_t **transaction_id, uint32_t *transaction_id_len, char **b64_AuthenticateServerResponse, struct es10b_authenticate_server_param *param, struct es10b_authenticate_server_param_user *param_user) { int fret = 0; uint8_t *reqbuf = NULL; uint32_t reqlen; uint8_t *respbuf = NULL; unsigned resplen; uint8_t imei[8]; uint8_t *serverSigned1 = NULL, *serverSignature1 = NULL, *euiccCiPKIdToBeUsed = NULL, *serverCertificate = NULL; int serverSigned1_len, serverSignature1_len, euiccCiPKIdToBeUsed_len, serverCertificate_len; struct euicc_derutil_node n_request, n_serverSigned1, n_transactionId, n_serverSignature1, n_euiccCiPKIdToBeUsed, n_serverCertificate, n_CtxParams1, n_matchingId, n_deviceInfo, n_tac, n_deviceCapabilities, n_imei; *transaction_id = NULL; *transaction_id_len = 0; *b64_AuthenticateServerResponse = NULL; memset(&n_request, 0, sizeof(n_request)); memset(&n_serverSigned1, 0, sizeof(n_serverSigned1)); memset(&n_serverSignature1, 0, sizeof(n_serverSignature1)); memset(&n_euiccCiPKIdToBeUsed, 0, sizeof(n_euiccCiPKIdToBeUsed)); memset(&n_serverCertificate, 0, sizeof(n_serverCertificate)); memset(&n_CtxParams1, 0, sizeof(n_CtxParams1)); memset(&n_matchingId, 0, sizeof(n_matchingId)); memset(&n_deviceInfo, 0, sizeof(n_deviceInfo)); memset(&n_tac, 0, sizeof(n_tac)); memset(&n_deviceCapabilities, 0, sizeof(n_deviceCapabilities)); memset(&n_imei, 0, sizeof(n_imei)); serverSigned1 = malloc(euicc_base64_decode_len(param->b64_serverSigned1)); if (!serverSigned1) { goto err; } serverSignature1 = malloc(euicc_base64_decode_len(param->b64_serverSignature1)); if (!serverSignature1) { goto err; } euiccCiPKIdToBeUsed = malloc(euicc_base64_decode_len(param->b64_euiccCiPKIdToBeUsed)); if (!euiccCiPKIdToBeUsed) { goto err; } serverCertificate = malloc(euicc_base64_decode_len(param->b64_serverCertificate)); if (!serverCertificate) { goto err; } if ((serverSigned1_len = euicc_base64_decode(serverSigned1, param->b64_serverSigned1)) < 0) { goto err; } if ((serverSignature1_len = euicc_base64_decode(serverSignature1, param->b64_serverSignature1)) < 0) { goto err; } if ((euiccCiPKIdToBeUsed_len = euicc_base64_decode(euiccCiPKIdToBeUsed, param->b64_euiccCiPKIdToBeUsed)) < 0) { goto err; } if ((serverCertificate_len = euicc_base64_decode(serverCertificate, param->b64_serverCertificate)) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&n_serverSigned1, 0x30, serverSigned1, serverSigned1_len) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&n_transactionId, 0x80, n_serverSigned1.value, n_serverSigned1.length) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&n_serverSignature1, 0x5F37, serverSignature1, serverSignature1_len) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&n_euiccCiPKIdToBeUsed, 0x04, euiccCiPKIdToBeUsed, euiccCiPKIdToBeUsed_len) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&n_serverCertificate, 0x30, serverCertificate, serverCertificate_len) < 0) { goto err; } *transaction_id_len = n_transactionId.length; *transaction_id = malloc(n_transactionId.length); if (!(*transaction_id)) { goto err; } memcpy(*transaction_id, n_transactionId.value, n_transactionId.length); n_request.tag = 0xBF38; n_request.pack.child = &n_serverSigned1; n_serverSigned1.pack.next = &n_serverSignature1; n_serverSignature1.pack.next = &n_euiccCiPKIdToBeUsed; n_euiccCiPKIdToBeUsed.pack.next = &n_serverCertificate; n_serverCertificate.pack.next = &n_CtxParams1; n_CtxParams1.tag = 0xA0; n_deviceInfo.tag = 0xA1; n_deviceInfo.pack.child = &n_tac; n_tac.tag = 0x80; n_tac.value = imei; n_tac.length = 4; n_tac.pack.next = &n_deviceCapabilities; n_deviceCapabilities.tag = 0xA1; if (param_user->imei) { int imei_len; imei_len = euicc_hexutil_gsmbcd2bin(imei, sizeof(imei), param_user->imei, 0); if (imei_len < 0) { goto err; } n_deviceCapabilities.pack.next = &n_imei; n_imei.tag = 0x82; n_imei.value = imei; n_imei.length = imei_len; } else { memcpy(imei, (uint8_t[]){0x35, 0x29, 0x06, 0x11}, 4); } if (param_user->matchingId) { n_CtxParams1.pack.child = &n_matchingId; n_matchingId.tag = 0x80; n_matchingId.value = (const uint8_t *)param_user->matchingId; n_matchingId.length = strlen(param_user->matchingId); n_matchingId.pack.next = &n_deviceInfo; } else { n_CtxParams1.pack.child = &n_deviceInfo; } if (euicc_derutil_pack_alloc(&reqbuf, &reqlen, &n_request) < 0) { goto err; } free(serverSigned1); serverSigned1 = NULL; free(serverSignature1); serverSignature1 = NULL; free(euiccCiPKIdToBeUsed); euiccCiPKIdToBeUsed = NULL; free(serverCertificate); serverCertificate = NULL; if (es10x_command(ctx, &respbuf, &resplen, reqbuf, reqlen) < 0) { goto err; } free(reqbuf); reqbuf = NULL; *b64_AuthenticateServerResponse = malloc(euicc_base64_encode_len(resplen)); if (!(*b64_AuthenticateServerResponse)) { goto err; } if (euicc_base64_encode(*b64_AuthenticateServerResponse, respbuf, resplen) < 0) { goto err; } fret = 0; goto exit; err: fret = -1; free(*transaction_id); *transaction_id = NULL; *transaction_id_len = 0; free(*b64_AuthenticateServerResponse); *b64_AuthenticateServerResponse = NULL; exit: free(serverSigned1); serverSigned1 = NULL; free(serverSignature1); serverSignature1 = NULL; free(euiccCiPKIdToBeUsed); euiccCiPKIdToBeUsed = NULL; free(serverCertificate); serverCertificate = NULL; free(reqbuf); reqbuf = NULL; free(respbuf); respbuf = NULL; return fret; } int es10b_cancel_session_r(struct euicc_ctx *ctx, char **b64_CancelSessionResponse, struct es10b_cancel_session_param *param) { int fret = 0; struct euicc_derutil_node n_request, n_transactionId, n_reason; uint8_t reason_buf[sizeof(enum es10b_cancel_session_reason)]; uint32_t reason_buf_len = sizeof(reason_buf); uint32_t reqlen; uint8_t *respbuf = NULL; unsigned resplen; struct euicc_derutil_node tmpnode; if (euicc_derutil_convert_long2bin(reason_buf, &reason_buf_len, param->reason) < 0) { goto err; } memset(&n_request, 0, sizeof(n_request)); memset(&n_transactionId, 0, sizeof(n_transactionId)); memset(&n_reason, 0, sizeof(n_reason)); n_request.tag = 0xBF41; // CancelSessionRequest n_request.pack.child = &n_transactionId; n_transactionId.tag = 0x80; n_transactionId.value = (const uint8_t *)param->transactionId; n_transactionId.length = param->transactionIdLen; n_transactionId.pack.next = &n_reason; n_reason.tag = 0x81; n_reason.value = reason_buf; n_reason.length = reason_buf_len; reqlen = sizeof(ctx->apdu._internal.request_buffer.body); if (euicc_derutil_pack(ctx->apdu._internal.request_buffer.body, &reqlen, &n_request)) { goto err; } if (es10x_command(ctx, &respbuf, &resplen, ctx->apdu._internal.request_buffer.body, reqlen) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&tmpnode, n_request.tag, respbuf, resplen)) { goto err; } *b64_CancelSessionResponse = malloc(euicc_base64_encode_len(tmpnode.self.length)); if (!(*b64_CancelSessionResponse)) { goto err; } if (euicc_base64_encode(*b64_CancelSessionResponse, tmpnode.self.ptr, tmpnode.self.length) < 0) { goto err; } goto exit; err: fret = -1; free(*b64_CancelSessionResponse); *b64_CancelSessionResponse = NULL; exit: free(respbuf); respbuf = NULL; return fret; } void es10b_prepare_download_param_free(struct es10b_prepare_download_param *param) { if (!param) { return; } free(param->b64_profileMetadata); free(param->b64_smdpCertificate); free(param->b64_smdpSignature2); free(param->b64_smdpSigned2); memset(param, 0x00, sizeof(*param)); } void es10b_authenticate_server_param_free(struct es10b_authenticate_server_param *param) { if (!param) { return; } free(param->b64_euiccCiPKIdToBeUsed); free(param->b64_serverCertificate); free(param->b64_serverSignature1); free(param->b64_serverSigned1); memset(param, 0x00, sizeof(*param)); } int es10b_prepare_download(struct euicc_ctx *ctx, const char *confirmationCode) { int fret; struct es10b_prepare_download_param_user param_user = { .confirmationCode = confirmationCode, }; if (ctx->http._internal.b64_prepare_download_response) { return -1; } if (ctx->http._internal.prepare_download_param == NULL) { return -1; } fret = es10b_prepare_download_r(ctx, &ctx->http._internal.b64_prepare_download_response, ctx->http._internal.prepare_download_param, ¶m_user); if (fret < 0) { ctx->http._internal.b64_prepare_download_response = NULL; return fret; } es10b_prepare_download_param_free(ctx->http._internal.prepare_download_param); free(ctx->http._internal.prepare_download_param); ctx->http._internal.prepare_download_param = NULL; return fret; } int es10b_load_bound_profile_package(struct euicc_ctx *ctx, struct es10b_load_bound_profile_package_result *result) { int fret; if (ctx->http._internal.b64_bound_profile_package == NULL) { return -1; } fret = es10b_load_bound_profile_package_r(ctx, result, ctx->http._internal.b64_bound_profile_package); if (fret < 0) { return fret; } free(ctx->http._internal.b64_bound_profile_package); ctx->http._internal.b64_bound_profile_package = NULL; return fret; } int es10b_get_euicc_challenge_and_info(struct euicc_ctx *ctx) { int fret; if (ctx->http._internal.b64_euicc_challenge) { return -1; } if (ctx->http._internal.b64_euicc_info_1) { return -1; } fret = es10b_get_euicc_challenge_r(ctx, &ctx->http._internal.b64_euicc_challenge); if (fret < 0) { goto err; } fret = es10b_get_euicc_info_r(ctx, &ctx->http._internal.b64_euicc_info_1); if (fret < 0) { goto err; } return fret; err: free(ctx->http._internal.b64_euicc_challenge); ctx->http._internal.b64_euicc_challenge = NULL; free(ctx->http._internal.b64_euicc_info_1); ctx->http._internal.b64_euicc_info_1 = NULL; return -1; } int es10b_authenticate_server(struct euicc_ctx *ctx, const char *matchingId, const char *imei) { int fret; struct es10b_authenticate_server_param_user param_user = { .matchingId = matchingId, .imei = imei, }; if (ctx->http._internal.b64_authenticate_server_response) { return -1; } if (ctx->http._internal.authenticate_server_param == NULL) { return -1; } fret = es10b_authenticate_server_r(ctx, &ctx->http._internal.transaction_id_bin, &ctx->http._internal.transaction_id_bin_len, &ctx->http._internal.b64_authenticate_server_response, ctx->http._internal.authenticate_server_param, ¶m_user); if (fret < 0) { ctx->http._internal.b64_authenticate_server_response = NULL; return fret; } es10b_authenticate_server_param_free(ctx->http._internal.authenticate_server_param); free(ctx->http._internal.authenticate_server_param); ctx->http._internal.authenticate_server_param = NULL; return fret; } int es10b_cancel_session(struct euicc_ctx *ctx, enum es10b_cancel_session_reason reason) { int fret; struct es10b_cancel_session_param param = { .transactionId = ctx->http._internal.transaction_id_bin, .transactionIdLen = ctx->http._internal.transaction_id_bin_len, .reason = reason, }; if (ctx->http._internal.transaction_id_bin == NULL) { return -1; } if (ctx->http._internal.transaction_id_bin_len == 0) { return -1; } if (ctx->http._internal.b64_cancel_session_response) { return -1; } fret = es10b_cancel_session_r(ctx, &ctx->http._internal.b64_cancel_session_response, ¶m); if (fret < 0) { ctx->http._internal.b64_cancel_session_response = NULL; } return fret; } int es10b_list_notification(struct euicc_ctx *ctx, struct es10b_notification_metadata_list **notificationMetadataList) { int fret = 0; struct euicc_derutil_node n_request = { .tag = 0xBF28, // ListNotificationRequest }; uint32_t reqlen; uint8_t *respbuf = NULL; unsigned resplen; struct euicc_derutil_node tmpnode, n_notificationMetadataList, n_NotificationMetadata; struct es10b_notification_metadata_list *list_wptr = NULL; *notificationMetadataList = NULL; reqlen = sizeof(ctx->apdu._internal.request_buffer.body); if (euicc_derutil_pack(ctx->apdu._internal.request_buffer.body, &reqlen, &n_request)) { goto err; } if (es10x_command(ctx, &respbuf, &resplen, ctx->apdu._internal.request_buffer.body, reqlen) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&tmpnode, n_request.tag, respbuf, resplen) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&n_notificationMetadataList, 0xA0, tmpnode.value, tmpnode.length) < 0) { goto err; } n_NotificationMetadata.self.ptr = n_notificationMetadataList.value; n_NotificationMetadata.self.length = 0; while (euicc_derutil_unpack_next(&n_NotificationMetadata, &n_NotificationMetadata, n_notificationMetadataList.value, n_notificationMetadataList.length) == 0) { struct es10b_notification_metadata_list *p; if (n_NotificationMetadata.tag != 0xBF2F) { continue; } p = malloc(sizeof(struct es10b_notification_metadata_list)); if (!p) { goto err; } memset(p, 0, sizeof(*p)); tmpnode.self.ptr = n_NotificationMetadata.value; tmpnode.self.length = 0; p->profileManagementOperation = ES10B_PROFILE_MANAGEMENT_OPERATION_NULL; while ( euicc_derutil_unpack_next(&tmpnode, &tmpnode, n_NotificationMetadata.value, n_NotificationMetadata.length) == 0) { switch (tmpnode.tag) { case 0x80: p->seqNumber = euicc_derutil_convert_bin2long(tmpnode.value, tmpnode.length); break; case 0x81: if (tmpnode.length >= 2) { switch (tmpnode.value[1]) { case ES10B_PROFILE_MANAGEMENT_OPERATION_INSTALL: case ES10B_PROFILE_MANAGEMENT_OPERATION_ENABLE: case ES10B_PROFILE_MANAGEMENT_OPERATION_DISABLE: case ES10B_PROFILE_MANAGEMENT_OPERATION_DELETE: p->profileManagementOperation = tmpnode.value[1]; break; default: p->profileManagementOperation = ES10B_PROFILE_MANAGEMENT_OPERATION_UNDEFINED; break; } } break; case 0x0C: p->notificationAddress = malloc(tmpnode.length + 1); if (p->notificationAddress) { memcpy(p->notificationAddress, tmpnode.value, tmpnode.length); p->notificationAddress[tmpnode.length] = '\0'; } break; case 0x5A: p->iccid = malloc((tmpnode.length * 2) + 1); if (p->iccid) { if (euicc_hexutil_bin2gsmbcd(p->iccid, (tmpnode.length * 2) + 1, tmpnode.value, tmpnode.length) < 0) { free(p->iccid); p->iccid = NULL; } } break; } } if (*notificationMetadataList == NULL) { *notificationMetadataList = p; } else { list_wptr->next = p; } list_wptr = p; } goto exit; err: fret = -1; es10b_notification_metadata_list_free_all(*notificationMetadataList); exit: free(respbuf); respbuf = NULL; return fret; } int es10b_retrieve_notifications_list(struct euicc_ctx *ctx, struct es10b_pending_notification *PendingNotification, unsigned long seqNumber) { int fret = 0; uint8_t seqNumber_buf[sizeof(seqNumber)]; uint32_t seqNumber_buf_len = sizeof(seqNumber_buf); struct euicc_derutil_node n_request, n_searchCriteria, n_seqNumber; uint32_t reqlen; uint8_t *respbuf = NULL; unsigned resplen; struct euicc_derutil_node tmpnode, n_PendingNotification, n_NotificationMetadata; memset(PendingNotification, 0, sizeof(struct es10b_pending_notification)); if (euicc_derutil_convert_long2bin(seqNumber_buf, &seqNumber_buf_len, seqNumber) < 0) { goto err; } memset(&n_request, 0, sizeof(n_request)); memset(&n_searchCriteria, 0, sizeof(n_searchCriteria)); memset(&n_seqNumber, 0, sizeof(n_seqNumber)); n_request.tag = 0xBF2B; // RetrieveNotificationsListRequest n_request.pack.child = &n_searchCriteria; n_searchCriteria.tag = 0xA0; // searchCriteria n_searchCriteria.pack.child = &n_seqNumber; n_seqNumber.tag = 0x80; // seqNumber n_seqNumber.length = seqNumber_buf_len; n_seqNumber.value = seqNumber_buf; reqlen = sizeof(ctx->apdu._internal.request_buffer.body); if (euicc_derutil_pack(ctx->apdu._internal.request_buffer.body, &reqlen, &n_request)) { goto err; } if (es10x_command(ctx, &respbuf, &resplen, ctx->apdu._internal.request_buffer.body, reqlen) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&tmpnode, n_request.tag, respbuf, resplen) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&tmpnode, 0xA0, tmpnode.value, tmpnode.length) < 0) { goto err; } if (euicc_derutil_unpack_find_alias_tags(&n_PendingNotification, (uint16_t[]){0xBF37, 0x30}, 2, tmpnode.value, tmpnode.length) < 0) { goto err; } switch (n_PendingNotification.tag) { case 0xBF37: // profileInstallationResult if (euicc_derutil_unpack_find_tag(&tmpnode, 0xBF27, n_PendingNotification.value, n_PendingNotification.length) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&n_NotificationMetadata, 0xBF2F, tmpnode.value, tmpnode.length) < 0) { goto err; } break; case 0x30: // otherSignedNotification if (euicc_derutil_unpack_find_tag(&n_NotificationMetadata, 0xBF2F, n_PendingNotification.value, n_PendingNotification.length) < 0) { goto err; } break; } if (euicc_derutil_unpack_find_tag(&tmpnode, 0x0C, n_NotificationMetadata.value, n_NotificationMetadata.length) < 0) { goto err; } PendingNotification->notificationAddress = malloc(tmpnode.length + 1); if (!PendingNotification->notificationAddress) { goto err; } memcpy(PendingNotification->notificationAddress, tmpnode.value, tmpnode.length); PendingNotification->notificationAddress[tmpnode.length] = '\0'; PendingNotification->b64_PendingNotification = malloc(euicc_base64_encode_len(n_PendingNotification.self.length)); if (!PendingNotification->b64_PendingNotification) { goto err; } if (euicc_base64_encode(PendingNotification->b64_PendingNotification, n_PendingNotification.self.ptr, n_PendingNotification.self.length) < 0) { goto err; } fret = 0; goto exit; err: fret = -1; es10b_pending_notification_free(PendingNotification); exit: free(respbuf); respbuf = NULL; return fret; } int es10b_remove_notification_from_list(struct euicc_ctx *ctx, unsigned long seqNumber) { int fret = 0; uint8_t seqNumber_buf[sizeof(seqNumber)]; uint32_t seqNumber_buf_len = sizeof(seqNumber_buf); struct euicc_derutil_node n_request, n_seqNumber; uint32_t reqlen; uint8_t *respbuf = NULL; unsigned resplen; struct euicc_derutil_node tmpnode; if (euicc_derutil_convert_long2bin(seqNumber_buf, &seqNumber_buf_len, seqNumber) < 0) { goto err; } memset(&n_request, 0, sizeof(n_request)); memset(&n_seqNumber, 0, sizeof(n_seqNumber)); n_request.tag = 0xBF30; // NotificationSentRequest n_request.pack.child = &n_seqNumber; n_seqNumber.tag = 0x80; // seqNumber n_seqNumber.length = seqNumber_buf_len; n_seqNumber.value = seqNumber_buf; reqlen = sizeof(ctx->apdu._internal.request_buffer.body); if (euicc_derutil_pack(ctx->apdu._internal.request_buffer.body, &reqlen, &n_request)) { goto err; } if (es10x_command(ctx, &respbuf, &resplen, ctx->apdu._internal.request_buffer.body, reqlen) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&tmpnode, n_request.tag, respbuf, resplen) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&tmpnode, 0x80, tmpnode.value, tmpnode.length) < 0) { goto err; } fret = euicc_derutil_convert_bin2long(tmpnode.value, tmpnode.length); goto exit; err: fret = -1; exit: free(respbuf); respbuf = NULL; return fret; } void es10b_notification_metadata_list_free_all(struct es10b_notification_metadata_list *notificationMetadataList) { while (notificationMetadataList) { struct es10b_notification_metadata_list *next = notificationMetadataList->next; free(notificationMetadataList->notificationAddress); free(notificationMetadataList->iccid); free(notificationMetadataList); notificationMetadataList = next; } } void es10b_pending_notification_free(struct es10b_pending_notification *PendingNotification) { free(PendingNotification->notificationAddress); free(PendingNotification->b64_PendingNotification); memset(PendingNotification, 0, sizeof(struct es10b_pending_notification)); } int es10b_get_rat(struct euicc_ctx *ctx, struct es10b_rat **ratList) { int fret; struct euicc_derutil_node n_request = { .tag = 0xBF43, // GetRatRequest }; uint32_t reqlen; uint8_t *respbuf = NULL; unsigned resplen; struct es10b_rat *rat_list_wptr = NULL; struct euicc_derutil_node tmpnode, tmpchildnode, n_profile; *ratList = NULL; reqlen = sizeof(ctx->apdu._internal.request_buffer.body); if (euicc_derutil_pack(ctx->apdu._internal.request_buffer.body, &reqlen, &n_request)) { goto err; } if (es10x_command(ctx, &respbuf, &resplen, ctx->apdu._internal.request_buffer.body, reqlen) < 0) { goto err; } if (resplen == 0) { goto err; } // GetRatResponse if (euicc_derutil_unpack_find_tag(&tmpnode, 0xBF43, respbuf, resplen) < 0) { goto err; } // RulesAuthorisationTable if (euicc_derutil_unpack_find_tag(&tmpnode, 0xA0, tmpnode.value, tmpnode.length) < 0) { goto err; } n_profile.self.ptr = tmpnode.value; n_profile.self.length = 0; // ProfilePolicyAuthorisationRule while (euicc_derutil_unpack_next(&n_profile, &n_profile, tmpnode.value, tmpnode.length) == 0) { struct es10b_rat *rat; tmpchildnode.self.ptr = n_profile.value; tmpchildnode.self.length = 0; rat = malloc(sizeof(struct es10b_rat)); if (!rat) { goto err; } memset(rat, 0, sizeof(*rat)); while (euicc_derutil_unpack_next(&tmpchildnode, &tmpchildnode, n_profile.value, n_profile.length) == 0) { switch (tmpchildnode.tag) { case 0x80: // ppr ids { static const char *desc[] = {"pprUpdateControl", "ppr1", "ppr2", "ppr3", NULL}; if (euicc_derutil_convert_bin2bits_str(&rat->pprIds, tmpchildnode.value, tmpchildnode.length, desc)) { goto err; } } break; case 0xA1: { // allowed operators struct euicc_derutil_node n_allowed_operator, n_operator; struct es10b_operation_id *operations_wptr = NULL; struct es10b_operation_id *p; n_allowed_operator.self.ptr = tmpchildnode.value; n_allowed_operator.self.length = 0; while (euicc_derutil_unpack_next(&n_allowed_operator, &n_allowed_operator, tmpchildnode.value, tmpchildnode.length) == 0) { p = malloc(sizeof(struct es10b_operation_id)); if (!p) { goto err; } memset(p, 0, sizeof(*p)); n_operator.self.ptr = n_allowed_operator.value; n_operator.self.length = 0; while (euicc_derutil_unpack_next(&n_operator, &n_operator, n_allowed_operator.value, n_allowed_operator.length) == 0) { if (n_operator.length == 0) { continue; } switch (n_operator.tag) { case 0x80: // mcc_mnc p->plmn = malloc((n_operator.length * 2) + 1); euicc_hexutil_bin2hex(p->plmn, (n_operator.length * 2) + 1, n_operator.value, n_operator.length); break; case 0x81: // gid1 p->gid1 = malloc((n_operator.length * 2) + 1); euicc_hexutil_bin2hex(p->gid1, (n_operator.length * 2) + 1, n_operator.value, n_operator.length); break; case 0x82: // gid2 p->gid2 = malloc((n_operator.length * 2) + 1); euicc_hexutil_bin2hex(p->gid2, (n_operator.length * 2) + 1, n_operator.value, n_operator.length); break; } } if (operations_wptr == NULL) { operations_wptr = p; } else { operations_wptr->next = p; } } rat->allowedOperators = operations_wptr; } break; case 0x82: { // ppr flags static const char *desc[] = {"consentRequired", NULL}; if (euicc_derutil_convert_bin2bits_str(&rat->pprFlags, tmpchildnode.value, tmpchildnode.length, desc)) { goto err; } } break; } } if (*ratList == NULL) { *ratList = rat; } else { rat_list_wptr->next = rat; } rat_list_wptr = rat; } fret = 0; goto exit; err: fret = -1; es10b_rat_list_free_all(*ratList); *ratList = NULL; exit: free(respbuf); respbuf = NULL; return fret; } void es10b_rat_list_free_all(struct es10b_rat *ratList) { struct es10b_rat *next_rat; struct es10b_operation_id *next_operation_id; while (ratList) { next_rat = ratList->next; free(ratList->pprIds); while (ratList->allowedOperators) { next_operation_id = ratList->allowedOperators->next; free(ratList->allowedOperators->plmn); free(ratList->allowedOperators->gid1); free(ratList->allowedOperators->gid2); free(ratList->allowedOperators); ratList->allowedOperators = next_operation_id; } free(ratList->pprFlags); free(ratList); ratList = next_rat; } } estkme-group-lpac-c2fcf5e/euicc/es10b.h000066400000000000000000000142011504765665400200230ustar00rootroot00000000000000#pragma once #include #include "euicc.h" struct euicc_ctx; enum es10b_profile_management_operation { ES10B_PROFILE_MANAGEMENT_OPERATION_NULL = -1, ES10B_PROFILE_MANAGEMENT_OPERATION_INSTALL = 0x80, ES10B_PROFILE_MANAGEMENT_OPERATION_ENABLE = 0x40, ES10B_PROFILE_MANAGEMENT_OPERATION_DISABLE = 0x20, ES10B_PROFILE_MANAGEMENT_OPERATION_DELETE = 0x10, ES10B_PROFILE_MANAGEMENT_OPERATION_UNDEFINED = 0xFF, }; enum es10b_bpp_command_id { ES10B_BPP_COMMAND_ID_INITIALISE_SECURE_CHANNEL = 0, ES10B_BPP_COMMAND_ID_CONFIGURE_ISDP = 1, ES10B_BPP_COMMAND_ID_STORE_METADATA = 2, ES10B_BPP_COMMAND_ID_STORE_METADATA2 = 3, ES10B_BPP_COMMAND_ID_REPLACE_SESSION_KEYS = 4, ES10B_BPP_COMMAND_ID_LOAD_PROFILE_ELEMENTS = 5, ES10B_BPP_COMMAND_ID_UNDEFINED = 0xFF, }; enum es10b_error_reason { ES10B_ERROR_REASON_INCORRECT_INPUT_VALUES = 1, ES10B_ERROR_REASON_INVALID_SIGNATURE = 2, ES10B_ERROR_REASON_INVALID_TRANSACTION_ID = 3, ES10B_ERROR_REASON_UNSUPPORTED_CRT_VALUES = 4, ES10B_ERROR_REASON_UNSUPPORTED_REMOTE_OPERATION_TYPE = 5, ES10B_ERROR_REASON_UNSUPPORTED_PROFILE_CLASS = 6, ES10B_ERROR_REASON_SCP03T_STRUCTURE_ERROR = 7, ES10B_ERROR_REASON_SCP03T_SECURITY_ERROR = 8, ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_ICCID_ALREADY_EXISTS_ON_EUICC = 9, ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_INSUFFICIENT_MEMORY_FOR_PROFILE = 10, ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_INTERRUPTION = 11, ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_PE_PROCESSING_ERROR = 12, ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_DATA_MISMATCH = 13, // For compatibility, see https://github.com/estkme-group/lpac/pull/246 ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_ICCID_MISMATCH = 13, ES10B_ERROR_REASON_TEST_PROFILE_INSTALL_FAILED_DUE_TO_INVALID_NAA_KEY = 14, ES10B_ERROR_REASON_PPR_NOT_ALLOWED = 15, ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_UNKNOWN_ERROR = 127, ES10B_ERROR_REASON_UNDEFINED = 0xFF, }; enum es10b_cancel_session_reason { ES10B_CANCEL_SESSION_REASON_ENDUSERREJECTION = 0, ES10B_CANCEL_SESSION_REASON_POSTPONED = 1, ES10B_CANCEL_SESSION_REASON_TIMEOUT = 2, ES10B_CANCEL_SESSION_REASON_PPRNOTALLOWED = 3, ES10B_CANCEL_SESSION_REASON_METADATAMISMATCH = 4, ES10B_CANCEL_SESSION_REASON_LOADBPPEXECUTIONERROR = 5, ES10B_CANCEL_SESSION_REASON_UNDEFINED = 127 }; struct es10b_load_bound_profile_package_result { unsigned long seqNumber; enum es10b_bpp_command_id bppCommandId; enum es10b_error_reason errorReason; }; struct es10b_prepare_download_param { char *b64_profileMetadata; char *b64_smdpSigned2; char *b64_smdpSignature2; char *b64_smdpCertificate; }; struct es10b_prepare_download_param_user { const char *confirmationCode; }; struct es10b_notification_metadata_list { unsigned long seqNumber; enum es10b_profile_management_operation profileManagementOperation; char *notificationAddress; char *iccid; struct es10b_notification_metadata_list *next; }; struct es10b_pending_notification { char *notificationAddress; char *b64_PendingNotification; }; struct es10b_authenticate_server_param { char *b64_serverSigned1; char *b64_serverSignature1; char *b64_euiccCiPKIdToBeUsed; char *b64_serverCertificate; }; struct es10b_authenticate_server_param_user { const char *matchingId; const char *imei; }; struct es10b_cancel_session_param { const uint8_t *transactionId; uint8_t transactionIdLen; enum es10b_cancel_session_reason reason; }; struct es10b_rat { const char **pprIds; struct es10b_operation_id *allowedOperators; const char **pprFlags; struct es10b_rat *next; }; struct es10b_operation_id { char *plmn; char *gid1; char *gid2; struct es10b_operation_id *next; }; int es10b_prepare_download_r(struct euicc_ctx *ctx, char **b64_PrepareDownloadResponse, struct es10b_prepare_download_param *param, struct es10b_prepare_download_param_user *param_user); int es10b_load_bound_profile_package_r(struct euicc_ctx *ctx, struct es10b_load_bound_profile_package_result *result, const char *b64_BoundProfilePackage); int es10b_get_euicc_challenge_r(struct euicc_ctx *ctx, char **b64_euiccChallenge); int es10b_get_euicc_info_r(struct euicc_ctx *ctx, char **b64_EUICCInfo1); int es10b_authenticate_server_r(struct euicc_ctx *ctx, uint8_t **transaction_id, uint32_t *transaction_id_len, char **b64_AuthenticateServerResponse, struct es10b_authenticate_server_param *param, struct es10b_authenticate_server_param_user *param_user); int es10b_cancel_session_r(struct euicc_ctx *ctx, char **b64_CancelSessionResponse, struct es10b_cancel_session_param *param); void es10b_prepare_download_param_free(struct es10b_prepare_download_param *param); void es10b_authenticate_server_param_free(struct es10b_authenticate_server_param *param); int es10b_prepare_download(struct euicc_ctx *ctx, const char *confirmationCode); int es10b_load_bound_profile_package(struct euicc_ctx *ctx, struct es10b_load_bound_profile_package_result *result); int es10b_get_euicc_challenge_and_info(struct euicc_ctx *ctx); int es10b_authenticate_server(struct euicc_ctx *ctx, const char *matchingId, const char *imei); int es10b_cancel_session(struct euicc_ctx *ctx, enum es10b_cancel_session_reason reason); int es10b_list_notification(struct euicc_ctx *ctx, struct es10b_notification_metadata_list **notificationMetadataList); int es10b_retrieve_notifications_list(struct euicc_ctx *ctx, struct es10b_pending_notification *PendingNotification, unsigned long seqNumber); int es10b_remove_notification_from_list(struct euicc_ctx *ctx, unsigned long seqNumber); void es10b_notification_metadata_list_free_all(struct es10b_notification_metadata_list *notificationMetadataList); void es10b_pending_notification_free(struct es10b_pending_notification *PendingNotification); int es10b_get_rat(struct euicc_ctx *ctx, struct es10b_rat **ratList); void es10b_rat_list_free_all(struct es10b_rat *ratList); estkme-group-lpac-c2fcf5e/euicc/es10c.c000066400000000000000000000325641504765665400200330ustar00rootroot00000000000000#include "es10c.h" #include "euicc.private.h" #include "base64.h" #include "derutil.h" #include "hexutil.h" #include #include #include #include #include int es10c_get_profiles_info(struct euicc_ctx *ctx, struct es10c_profile_info_list **profileInfoList) { int fret = 0; struct euicc_derutil_node n_request = { .tag = 0xBF2D, // ProfileInfoListRequest }; uint32_t reqlen; uint8_t *respbuf = NULL; unsigned resplen; struct euicc_derutil_node tmpnode, n_profileInfoListOk, n_ProfileInfo; struct es10c_profile_info_list *list_wptr = NULL; int tmpint; *profileInfoList = NULL; reqlen = sizeof(ctx->apdu._internal.request_buffer.body); if (euicc_derutil_pack(ctx->apdu._internal.request_buffer.body, &reqlen, &n_request)) { goto err; } if (es10x_command(ctx, &respbuf, &resplen, ctx->apdu._internal.request_buffer.body, reqlen) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&tmpnode, n_request.tag, respbuf, resplen) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&n_profileInfoListOk, 0xA0, tmpnode.value, tmpnode.length) < 0) { goto err; } n_ProfileInfo.self.ptr = n_profileInfoListOk.value; n_ProfileInfo.self.length = 0; while ( euicc_derutil_unpack_next(&n_ProfileInfo, &n_ProfileInfo, n_profileInfoListOk.value, n_profileInfoListOk.length) == 0) { struct es10c_profile_info_list *p; if (n_ProfileInfo.tag != 0xE3) { continue; } p = malloc(sizeof(struct es10c_profile_info_list)); if (!p) { goto err; } memset(p, 0, sizeof(*p)); tmpnode.self.ptr = n_ProfileInfo.value; tmpnode.self.length = 0; p->profileState = ES10C_PROFILE_STATE_NULL; p->profileClass = ES10C_PROFILE_CLASS_NULL; p->iconType = ES10C_ICON_TYPE_NULL; while (euicc_derutil_unpack_next(&tmpnode, &tmpnode, n_ProfileInfo.value, n_ProfileInfo.length) == 0) { switch (tmpnode.tag) { case 0x5A: euicc_hexutil_bin2gsmbcd(p->iccid, sizeof(p->iccid), tmpnode.value, tmpnode.length); break; case 0x4F: euicc_hexutil_bin2hex(p->isdpAid, sizeof(p->isdpAid), tmpnode.value, tmpnode.length); break; case 0x9F70: tmpint = euicc_derutil_convert_bin2long(tmpnode.value, tmpnode.length); switch (tmpint) { case ES10C_PROFILE_STATE_DISABLED: case ES10C_PROFILE_STATE_ENABLED: p->profileState = tmpint; break; default: p->profileState = ES10C_PROFILE_STATE_UNDEFINED; break; } break; case 0x90: p->profileNickname = malloc(tmpnode.length + 1); if (p->profileNickname) { memcpy(p->profileNickname, tmpnode.value, tmpnode.length); p->profileNickname[tmpnode.length] = '\0'; } break; case 0x91: p->serviceProviderName = malloc(tmpnode.length + 1); if (p->serviceProviderName) { memcpy(p->serviceProviderName, tmpnode.value, tmpnode.length); p->serviceProviderName[tmpnode.length] = '\0'; } break; case 0x92: p->profileName = malloc(tmpnode.length + 1); if (p->profileName) { memcpy(p->profileName, tmpnode.value, tmpnode.length); p->profileName[tmpnode.length] = '\0'; } break; case 0x93: tmpint = euicc_derutil_convert_bin2long(tmpnode.value, tmpnode.length); switch (tmpint) { case ES10C_ICON_TYPE_JPEG: case ES10C_ICON_TYPE_PNG: p->iconType = tmpint; break; default: p->iconType = ES10C_ICON_TYPE_UNDEFINED; break; } break; case 0x94: p->icon = malloc(euicc_base64_encode_len(tmpnode.length)); if (p->icon) { euicc_base64_encode(p->icon, tmpnode.value, tmpnode.length); } break; case 0x95: tmpint = euicc_derutil_convert_bin2long(tmpnode.value, tmpnode.length); switch (tmpint) { case ES10C_PROFILE_CLASS_TEST: case ES10C_PROFILE_CLASS_PROVISIONING: case ES10C_PROFILE_CLASS_OPERATIONAL: p->profileClass = tmpint; break; default: p->profileClass = ES10C_PROFILE_CLASS_UNDEFINED; break; } break; case 0xB6: case 0xB7: case 0xB8: case 0x99: fprintf(stderr, "\n[PLEASE REPORT][TODO][TAG %02X]: ", tmpnode.tag); for (uint32_t i = 0; i < tmpnode.self.length; i++) { fprintf(stderr, "%02X ", tmpnode.self.ptr[i]); } fprintf(stderr, "\n"); break; } } if (*profileInfoList == NULL) { *profileInfoList = p; } else { list_wptr->next = p; } list_wptr = p; } goto exit; err: fret = -1; es10c_profile_info_list_free_all(*profileInfoList); exit: free(respbuf); respbuf = NULL; return fret; } static int es10c_enable_disable_delete_profile(struct euicc_ctx *ctx, uint16_t op_tag, const char *str_id, uint8_t refreshFlag) { int fret = 0; uint8_t id[16]; int id_len; struct euicc_derutil_node n_request, n_choicer, n_profileIdentifierChoice, n_refreshFlag; uint32_t reqlen; uint8_t *respbuf = NULL; unsigned resplen; struct euicc_derutil_node tmpnode; memset(&n_request, 0, sizeof(n_request)); memset(&n_choicer, 0, sizeof(n_choicer)); memset(&n_profileIdentifierChoice, 0, sizeof(n_profileIdentifierChoice)); memset(&n_refreshFlag, 0, sizeof(n_refreshFlag)); if (strlen(str_id) == 32) { if ((id_len = euicc_hexutil_hex2bin(id, sizeof(id), str_id)) < 0) { return -1; } n_profileIdentifierChoice.tag = 0x4F; } else { if ((id_len = euicc_hexutil_gsmbcd2bin(id, sizeof(id), str_id, 10)) < 0) { return -1; } n_profileIdentifierChoice.tag = 0x5A; } n_profileIdentifierChoice.length = id_len; n_profileIdentifierChoice.value = id; if (refreshFlag & 0x80) { refreshFlag &= 0x7F; if (refreshFlag) { refreshFlag = 0xFF; } n_refreshFlag.tag = 0x81; n_refreshFlag.length = 1; n_refreshFlag.value = &refreshFlag; n_choicer.tag = 0xA0; n_choicer.pack.child = &n_profileIdentifierChoice; n_choicer.pack.next = &n_refreshFlag; n_request.pack.child = &n_choicer; } else { n_request.pack.child = &n_profileIdentifierChoice; } n_request.tag = op_tag; reqlen = sizeof(ctx->apdu._internal.request_buffer.body); if (euicc_derutil_pack(ctx->apdu._internal.request_buffer.body, &reqlen, &n_request)) { goto err; } if (es10x_command(ctx, &respbuf, &resplen, ctx->apdu._internal.request_buffer.body, reqlen) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&tmpnode, n_request.tag, respbuf, resplen) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&tmpnode, 0x80, tmpnode.value, tmpnode.length) < 0) { goto err; } fret = euicc_derutil_convert_bin2long(tmpnode.value, tmpnode.length); goto exit; err: fret = -1; exit: free(respbuf); respbuf = NULL; return fret; } int es10c_enable_profile(struct euicc_ctx *ctx, const char *id, uint8_t refreshFlag) { if (refreshFlag) { refreshFlag = 0xFF; } else { refreshFlag = 0x80; } return es10c_enable_disable_delete_profile(ctx, 0xBF31, id, refreshFlag); } int es10c_disable_profile(struct euicc_ctx *ctx, const char *id, uint8_t refreshFlag) { if (refreshFlag) { refreshFlag = 0xFF; } else { refreshFlag = 0x80; } return es10c_enable_disable_delete_profile(ctx, 0xBF32, id, refreshFlag); } int es10c_delete_profile(struct euicc_ctx *ctx, const char *id) { return es10c_enable_disable_delete_profile(ctx, 0xBF33, id, 0); } int es10c_euicc_memory_reset(struct euicc_ctx *ctx) { int fret = 0; struct euicc_derutil_node n_request = { .tag = 0xBF34, // EuiccMemoryResetRequest .pack = { .child = &(struct euicc_derutil_node){ .tag = 0x82, // resetOptions .length = 2, .value = (const uint8_t[]){0x05, 0xE0}, }, }, }; uint32_t reqlen; uint8_t *respbuf = NULL; unsigned resplen; struct euicc_derutil_node tmpnode; reqlen = sizeof(ctx->apdu._internal.request_buffer.body); if (euicc_derutil_pack(ctx->apdu._internal.request_buffer.body, &reqlen, &n_request)) { goto err; } if (es10x_command(ctx, &respbuf, &resplen, ctx->apdu._internal.request_buffer.body, reqlen) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&tmpnode, n_request.tag, respbuf, resplen) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&tmpnode, 0x80, tmpnode.value, tmpnode.length) < 0) { goto err; } fret = euicc_derutil_convert_bin2long(tmpnode.value, tmpnode.length); goto exit; err: fret = -1; exit: free(respbuf); respbuf = NULL; return fret; } int es10c_get_eid(struct euicc_ctx *ctx, char **eidValue) { int fret = 0; struct euicc_derutil_node n_request = { .tag = 0xBF3E, // GetEuiccDataRequest .pack = { .child = &(struct euicc_derutil_node){ .tag = 0x5C, // tagList .length = 1, .value = (const uint8_t[]){0x5A}, }, }, }; uint32_t reqlen; uint8_t *respbuf = NULL; unsigned resplen; struct euicc_derutil_node tmpnode; reqlen = sizeof(ctx->apdu._internal.request_buffer.body); if (euicc_derutil_pack(ctx->apdu._internal.request_buffer.body, &reqlen, &n_request)) { goto err; } if (es10x_command(ctx, &respbuf, &resplen, ctx->apdu._internal.request_buffer.body, reqlen) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&tmpnode, n_request.tag, respbuf, resplen)) { goto err; } if (euicc_derutil_unpack_find_tag(&tmpnode, 0x5A, tmpnode.value, tmpnode.length)) { goto err; } *eidValue = malloc((tmpnode.length * 2) + 1); if (*eidValue == NULL) { goto err; } euicc_hexutil_bin2hex(*eidValue, (tmpnode.length * 2) + 1, tmpnode.value, tmpnode.length); goto exit; err: fret = -1; free(*eidValue); *eidValue = NULL; exit: free(respbuf); respbuf = NULL; return fret; } int es10c_set_nickname(struct euicc_ctx *ctx, const char *iccid, const char *profileNickname) { int fret = 0; uint8_t asn1iccid[10]; struct euicc_derutil_node n_request, n_iccid, n_profileNickname; uint32_t reqlen; uint8_t *respbuf = NULL; unsigned resplen; struct euicc_derutil_node tmpnode; memset(&n_request, 0, sizeof(n_request)); memset(&n_iccid, 0, sizeof(n_iccid)); memset(&n_profileNickname, 0, sizeof(n_profileNickname)); if (euicc_hexutil_gsmbcd2bin(asn1iccid, sizeof(asn1iccid), iccid, 10) < 0) { goto err; } n_request.tag = 0xBF29; n_request.pack.child = &n_iccid; n_iccid.tag = 0x5A; n_iccid.length = sizeof(asn1iccid); n_iccid.value = asn1iccid; n_iccid.pack.next = &n_profileNickname; n_profileNickname.tag = 0x90; n_profileNickname.length = strlen(profileNickname); n_profileNickname.value = (const uint8_t *)profileNickname; reqlen = sizeof(ctx->apdu._internal.request_buffer.body); if (euicc_derutil_pack(ctx->apdu._internal.request_buffer.body, &reqlen, &n_request)) { goto err; } if (es10x_command(ctx, &respbuf, &resplen, ctx->apdu._internal.request_buffer.body, reqlen) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&tmpnode, n_request.tag, respbuf, resplen) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&tmpnode, 0x80, tmpnode.value, tmpnode.length) < 0) { goto err; } fret = euicc_derutil_convert_bin2long(tmpnode.value, tmpnode.length); goto exit; err: fret = -1; exit: free(respbuf); respbuf = NULL; return fret; } void es10c_profile_info_list_free_all(struct es10c_profile_info_list *profileInfoList) { while (profileInfoList) { struct es10c_profile_info_list *next = profileInfoList->next; free(profileInfoList->profileNickname); free(profileInfoList->serviceProviderName); free(profileInfoList->profileName); free(profileInfoList->icon); free(profileInfoList); profileInfoList = next; } } estkme-group-lpac-c2fcf5e/euicc/es10c.h000066400000000000000000000035571504765665400200400ustar00rootroot00000000000000#pragma once #include "euicc.h" enum es10c_profile_state { ES10C_PROFILE_STATE_NULL = -1, ES10C_PROFILE_STATE_DISABLED = 0, ES10C_PROFILE_STATE_ENABLED = 1, ES10C_PROFILE_STATE_UNDEFINED = 255, }; enum es10c_profile_class { ES10C_PROFILE_CLASS_NULL = -1, ES10C_PROFILE_CLASS_TEST = 0, ES10C_PROFILE_CLASS_PROVISIONING = 1, ES10C_PROFILE_CLASS_OPERATIONAL = 2, ES10C_PROFILE_CLASS_UNDEFINED = 255, }; enum es10c_icon_type { ES10C_ICON_TYPE_NULL = -1, ES10C_ICON_TYPE_JPEG = 0, ES10C_ICON_TYPE_PNG = 1, ES10C_ICON_TYPE_UNDEFINED = 255, }; struct es10c_profile_info_list { char iccid[(10 * 2) + 1]; char isdpAid[(16 * 2) + 1]; enum es10c_profile_state profileState; enum es10c_profile_class profileClass; char *profileNickname; char *serviceProviderName; char *profileName; enum es10c_icon_type iconType; char *icon; struct { char **profileManagementOperation; char *notificationAddress; } notificationConfigurationInfo; struct { char *mccmnc; char *gid1; char *gid2; } profileOwner; struct { char *dpOid; } dpProprietaryData; char **profilePolicyRules; struct es10c_profile_info_list *next; }; int es10c_get_profiles_info(struct euicc_ctx *ctx, struct es10c_profile_info_list **profileInfoList); int es10c_enable_profile(struct euicc_ctx *ctx, const char *id, uint8_t refreshFlag); int es10c_disable_profile(struct euicc_ctx *ctx, const char *id, uint8_t refreshFlag); int es10c_delete_profile(struct euicc_ctx *ctx, const char *id); int es10c_euicc_memory_reset(struct euicc_ctx *ctx); int es10c_get_eid(struct euicc_ctx *ctx, char **eidValue); int es10c_set_nickname(struct euicc_ctx *ctx, const char *iccid, const char *profileNickname); void es10c_profile_info_list_free_all(struct es10c_profile_info_list *profileInfoList); estkme-group-lpac-c2fcf5e/euicc/es10c_ex.c000066400000000000000000000302201504765665400205120ustar00rootroot00000000000000#define _GNU_SOURCE #include "es10c_ex.h" #include "euicc.private.h" #include #include #include #include "derutil.h" #include "hexutil.h" static int _versiontype2str(char **out, const uint8_t *buffer, uint8_t buffer_len) { if (buffer_len != 3) { return -1; } return asprintf(out, "%d.%d.%d", buffer[0], buffer[1], buffer[2]); } int es10c_ex_get_euiccinfo2(struct euicc_ctx *ctx, struct es10c_ex_euiccinfo2 *euiccinfo2) { int fret = 0; struct euicc_derutil_node n_request = { .tag = 0xBF22, // GetEuiccInfo2Request }; uint32_t reqlen; uint8_t *respbuf = NULL; unsigned resplen; struct euicc_derutil_node tmpnode, tmpchidnode, n_EUICCInfo2; memset(euiccinfo2, 0, sizeof(struct es10c_ex_euiccinfo2)); reqlen = sizeof(ctx->apdu._internal.request_buffer.body); if (euicc_derutil_pack(ctx->apdu._internal.request_buffer.body, &reqlen, &n_request) < 0) { goto err; } if (es10x_command(ctx, &respbuf, &resplen, ctx->apdu._internal.request_buffer.body, reqlen) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&n_EUICCInfo2, n_request.tag, respbuf, resplen) < 0) { goto err; } tmpnode.self.ptr = n_EUICCInfo2.value; tmpnode.self.length = 0; while (euicc_derutil_unpack_next(&tmpnode, &tmpnode, n_EUICCInfo2.value, n_EUICCInfo2.length) == 0) { switch (tmpnode.tag) { case 0x81: // profileVersion _versiontype2str(&euiccinfo2->profileVersion, tmpnode.value, tmpnode.length); break; case 0x82: // svn _versiontype2str(&euiccinfo2->svn, tmpnode.value, tmpnode.length); break; case 0x83: // euiccFirmwareVer _versiontype2str(&euiccinfo2->euiccFirmwareVer, tmpnode.value, tmpnode.length); break; case 0x84: // extCardResource tmpchidnode.self.ptr = tmpnode.value; tmpchidnode.self.length = 0; while (euicc_derutil_unpack_next(&tmpchidnode, &tmpchidnode, tmpnode.value, tmpnode.length) == 0) { switch (tmpchidnode.tag) { case 0x81: euiccinfo2->extCardResource.installedApplication = euicc_derutil_convert_bin2long(tmpchidnode.value, tmpchidnode.length); break; case 0x82: euiccinfo2->extCardResource.freeNonVolatileMemory = euicc_derutil_convert_bin2long(tmpchidnode.value, tmpchidnode.length); break; case 0x83: euiccinfo2->extCardResource.freeVolatileMemory = euicc_derutil_convert_bin2long(tmpchidnode.value, tmpchidnode.length); break; } } break; case 0x85: { // uiccCapability static const char *desc[] = {"contactlessSupport", "usimSupport", "isimSupport", "csimSupport", "akaMilenage", "akaCave", "akaTuak128", "akaTuak256", "rfu1", "rfu2", "gbaAuthenUsim", "gbaAuthenISim", "mbmsAuthenUsim", "eapClient", "javacard", "multos", "multipleUsimSupport", "multipleIsimSupport", "multipleCsimSupport", "berTlvFileSupport", "dfLinkSupport", "catTp", "getIdentity", "profile-a-x25519", "profile-b-p256", "suciCalculatorApi", NULL}; if (euicc_derutil_convert_bin2bits_str(&euiccinfo2->uiccCapability, tmpnode.value, tmpnode.length, desc)) { goto err; } } break; case 0x86: // ts102241Version _versiontype2str(&euiccinfo2->ts102241Version, tmpnode.value, tmpnode.length); break; case 0x87: // globalplatformVersion _versiontype2str(&euiccinfo2->globalplatformVersion, tmpnode.value, tmpnode.length); break; case 0x88: { // rspCapability static const char *desc[] = {"additionalProfile", "crlSupport", "rpmSupport", "testProfileSupport", "deviceInfoExtensibilitySupport", NULL}; if (euicc_derutil_convert_bin2bits_str(&euiccinfo2->rspCapability, tmpnode.value, tmpnode.length, desc)) { goto err; } } break; case 0xA9: { // euiccCiPKIdListForVerification int count; tmpchidnode.self.ptr = tmpnode.value; tmpchidnode.self.length = 0; count = 0; while (euicc_derutil_unpack_next(&tmpchidnode, &tmpchidnode, tmpnode.value, tmpnode.length) == 0) { count++; } euiccinfo2->euiccCiPKIdListForVerification = malloc((count + 1) * sizeof(char *)); if (!euiccinfo2->euiccCiPKIdListForVerification) { goto err; } memset(euiccinfo2->euiccCiPKIdListForVerification, 0, (count + 1) * sizeof(char *)); tmpchidnode.self.ptr = tmpnode.value; tmpchidnode.self.length = 0; count = 0; while (euicc_derutil_unpack_next(&tmpchidnode, &tmpchidnode, tmpnode.value, tmpnode.length) == 0) { euiccinfo2->euiccCiPKIdListForVerification[count] = malloc((tmpchidnode.length * 2 + 1) * sizeof(char)); if (!euiccinfo2->euiccCiPKIdListForVerification[count]) { goto err; } euicc_hexutil_bin2hex(euiccinfo2->euiccCiPKIdListForVerification[count], tmpchidnode.length * 2 + 1, tmpchidnode.value, tmpchidnode.length); count++; } } break; case 0xAA: { // euiccCiPKIdListForSigning int count; tmpchidnode.self.ptr = tmpnode.value; tmpchidnode.self.length = 0; count = 0; while (euicc_derutil_unpack_next(&tmpchidnode, &tmpchidnode, tmpnode.value, tmpnode.length) == 0) { count++; } euiccinfo2->euiccCiPKIdListForSigning = malloc((count + 1) * sizeof(char *)); if (!euiccinfo2->euiccCiPKIdListForSigning) { goto err; } memset(euiccinfo2->euiccCiPKIdListForSigning, 0, (count + 1) * sizeof(char *)); tmpchidnode.self.ptr = tmpnode.value; tmpchidnode.self.length = 0; count = 0; while (euicc_derutil_unpack_next(&tmpchidnode, &tmpchidnode, tmpnode.value, tmpnode.length) == 0) { euiccinfo2->euiccCiPKIdListForSigning[count] = malloc((tmpchidnode.length * 2 + 1) * sizeof(char)); if (!euiccinfo2->euiccCiPKIdListForSigning[count]) { goto err; } euicc_hexutil_bin2hex(euiccinfo2->euiccCiPKIdListForSigning[count], tmpchidnode.length * 2 + 1, tmpchidnode.value, tmpchidnode.length); count++; } } break; case 0xAB: { // euiccCategory switch (euicc_derutil_convert_bin2long(tmpnode.value, tmpnode.length)) { case 1: euiccinfo2->euiccCategory = "basicEuicc"; break; case 2: euiccinfo2->euiccCategory = "mediumEuicc"; break; case 3: euiccinfo2->euiccCategory = "contactlessEuicc"; break; case 0: default: euiccinfo2->euiccCategory = "other"; break; } } break; case 0x99: { // forbiddenProfilePolicyRules static const char *desc[] = {"pprUpdateControl", "ppr1", "ppr2", "ppr3", NULL}; if (euicc_derutil_convert_bin2bits_str(&euiccinfo2->forbiddenProfilePolicyRules, tmpnode.value, tmpnode.length, desc)) { goto err; } } break; case 0x04: // ppVersion _versiontype2str(&euiccinfo2->ppVersion, tmpnode.value, tmpnode.length); break; case 0x0C: // sasAcreditationNumber euiccinfo2->sasAcreditationNumber = malloc(tmpnode.length + 1); if (!euiccinfo2->sasAcreditationNumber) { goto err; } memcpy(euiccinfo2->sasAcreditationNumber, tmpnode.value, tmpnode.length); euiccinfo2->sasAcreditationNumber[tmpnode.length] = 0; break; case 0xAC: // certificationDataObject tmpchidnode.self.ptr = tmpnode.value; tmpchidnode.self.length = 0; while (euicc_derutil_unpack_next(&tmpchidnode, &tmpchidnode, tmpnode.value, tmpnode.length) == 0) { switch (tmpchidnode.tag) { case 0x80: euiccinfo2->certificationDataObject.platformLabel = malloc(tmpchidnode.length + 1); if (!euiccinfo2->certificationDataObject.platformLabel) { goto err; } memcpy(euiccinfo2->certificationDataObject.platformLabel, tmpchidnode.value, tmpchidnode.length); euiccinfo2->certificationDataObject.platformLabel[tmpchidnode.length] = 0; break; case 0x81: euiccinfo2->certificationDataObject.discoveryBaseURL = malloc(tmpchidnode.length + 1); if (!euiccinfo2->certificationDataObject.discoveryBaseURL) { goto err; } memcpy(euiccinfo2->certificationDataObject.discoveryBaseURL, tmpchidnode.value, tmpchidnode.length); euiccinfo2->certificationDataObject.discoveryBaseURL[tmpchidnode.length] = 0; break; } } break; } } fret = 0; goto exit; err: fret = -1; es10c_ex_euiccinfo2_free(euiccinfo2); exit: free(respbuf); respbuf = NULL; return fret; } void es10c_ex_euiccinfo2_free(struct es10c_ex_euiccinfo2 *euiccinfo2) { if (!euiccinfo2) { return; } free(euiccinfo2->profileVersion); free(euiccinfo2->svn); free(euiccinfo2->euiccFirmwareVer); free(euiccinfo2->uiccCapability); free(euiccinfo2->ts102241Version); free(euiccinfo2->globalplatformVersion); free(euiccinfo2->rspCapability); if (euiccinfo2->euiccCiPKIdListForVerification) { for (int i = 0; euiccinfo2->euiccCiPKIdListForVerification[i] != NULL; i++) { free(euiccinfo2->euiccCiPKIdListForVerification[i]); } free(euiccinfo2->euiccCiPKIdListForVerification); } if (euiccinfo2->euiccCiPKIdListForSigning) { for (int i = 0; euiccinfo2->euiccCiPKIdListForSigning[i] != NULL; i++) { free(euiccinfo2->euiccCiPKIdListForSigning[i]); } free(euiccinfo2->euiccCiPKIdListForSigning); } free(euiccinfo2->forbiddenProfilePolicyRules); free(euiccinfo2->ppVersion); free(euiccinfo2->sasAcreditationNumber); free(euiccinfo2->certificationDataObject.discoveryBaseURL); free(euiccinfo2->certificationDataObject.platformLabel); memset(euiccinfo2, 0, sizeof(struct es10c_ex_euiccinfo2)); } estkme-group-lpac-c2fcf5e/euicc/es10c_ex.h000066400000000000000000000015751504765665400205320ustar00rootroot00000000000000#pragma once #include "euicc.h" struct es10c_ex_euiccinfo2 { char *profileVersion; char *svn; char *euiccFirmwareVer; struct { uint32_t installedApplication; uint32_t freeNonVolatileMemory; uint32_t freeVolatileMemory; } extCardResource; const char **uiccCapability; char *ts102241Version; char *globalplatformVersion; const char **rspCapability; char **euiccCiPKIdListForVerification; char **euiccCiPKIdListForSigning; const char *euiccCategory; const char **forbiddenProfilePolicyRules; char *ppVersion; char *sasAcreditationNumber; struct { char *platformLabel; char *discoveryBaseURL; } certificationDataObject; }; int es10c_ex_get_euiccinfo2(struct euicc_ctx *ctx, struct es10c_ex_euiccinfo2 *euiccinfo2); void es10c_ex_euiccinfo2_free(struct es10c_ex_euiccinfo2 *euiccinfo2); estkme-group-lpac-c2fcf5e/euicc/es8p.c000066400000000000000000000076151504765665400177760ustar00rootroot00000000000000#include "es8p.h" #include "base64.h" #include "derutil.h" #include "hexutil.h" #include #include #include #include #include int es8p_metadata_parse(struct es8p_metadata **stru_metadata, const char *b64_Metadata) { int ret; uint8_t *metadata = NULL; int metadata_len = 0; struct euicc_derutil_node n_metadata, n_iter; struct es8p_metadata *p = NULL; *stru_metadata = NULL; memset(&n_metadata, 0x00, sizeof(n_metadata)); memset(&n_iter, 0x00, sizeof(n_iter)); metadata = malloc(euicc_base64_decode_len(b64_Metadata)); if (!metadata) { goto err; } if ((metadata_len = euicc_base64_decode(metadata, b64_Metadata)) < 0) { goto err; } if (euicc_derutil_unpack_find_tag(&n_metadata, 0xBF25, metadata, metadata_len) < 0) { goto err; } if (!(p = malloc(sizeof(struct es8p_metadata)))) { goto err; } memset(p, 0, sizeof(*p)); n_iter.self.ptr = n_metadata.value; n_iter.self.length = 0; p->profileClass = ES10C_PROFILE_CLASS_NULL; p->iconType = ES10C_ICON_TYPE_NULL; while (euicc_derutil_unpack_next(&n_iter, &n_iter, n_metadata.value, n_metadata.length) == 0) { int tmplong; switch (n_iter.tag) { case 0x5A: euicc_hexutil_bin2gsmbcd(p->iccid, sizeof(p->iccid), n_iter.value, n_iter.length); break; case 0x91: p->serviceProviderName = malloc(n_iter.length + 1); if (p->serviceProviderName) { memcpy(p->serviceProviderName, n_iter.value, n_iter.length); p->serviceProviderName[n_iter.length] = '\0'; } break; case 0x92: p->profileName = malloc(n_iter.length + 1); if (p->profileName) { memcpy(p->profileName, n_iter.value, n_iter.length); p->profileName[n_iter.length] = '\0'; } break; case 0x93: tmplong = euicc_derutil_convert_bin2long(n_iter.value, n_iter.length); switch (tmplong) { case ES10C_ICON_TYPE_JPEG: case ES10C_ICON_TYPE_PNG: p->iconType = tmplong; break; default: p->iconType = ES10C_ICON_TYPE_UNDEFINED; break; } break; case 0x94: p->icon = malloc(euicc_base64_encode_len(n_iter.length)); if (p->icon) { euicc_base64_encode(p->icon, n_iter.value, n_iter.length); } break; case 0x95: tmplong = euicc_derutil_convert_bin2long(n_iter.value, n_iter.length); switch (tmplong) { case ES10C_PROFILE_CLASS_TEST: case ES10C_PROFILE_CLASS_PROVISIONING: case ES10C_PROFILE_CLASS_OPERATIONAL: p->profileClass = tmplong; break; default: p->profileClass = ES10C_PROFILE_CLASS_UNDEFINED; break; } break; case 0xB6: case 0xB7: case 0x99: // fprintf(stderr, "\n[PLEASE REPORT][TODO][TAG %02X]: ", n_iter.tag); // for (uint32_t i = 0; i < n_iter.self.length; i++) // { // fprintf(stderr, "%02X ", n_iter.self.ptr[i]); // } // fprintf(stderr, "\n"); break; } } *stru_metadata = p; ret = 0; goto exit; err: ret = -1; free(*stru_metadata); *stru_metadata = NULL; free(p); p = NULL; exit: free(metadata); metadata = NULL; return ret; } void es8p_metadata_free(struct es8p_metadata **stru_metadata) { struct es8p_metadata *p = *stru_metadata; if (p == NULL) { return; } free(p->serviceProviderName); free(p->profileName); free(p->icon); free(p); *stru_metadata = NULL; } estkme-group-lpac-c2fcf5e/euicc/es8p.h000066400000000000000000000013161504765665400177730ustar00rootroot00000000000000#pragma once #include "es10c.h" #include "euicc.h" struct es8p_metadata { char iccid[(10 * 2) + 1]; char *serviceProviderName; char *profileName; enum es10c_icon_type iconType; char *icon; enum es10c_profile_class profileClass; struct { char **profileManagementOperation; char *notificationAddress; } notificationConfigurationInfo; struct { char *mccmnc; char *gid1; char *gid2; } profileOwner; struct { char *dpOid; } dpProprietaryData; char **profilePolicyRules; }; int es8p_metadata_parse(struct es8p_metadata **metadata, const char *b64_Metadata); void es8p_metadata_free(struct es8p_metadata **stru_metadata); estkme-group-lpac-c2fcf5e/euicc/es9p.c000066400000000000000000000460561504765665400200010ustar00rootroot00000000000000#include "es9p.h" #include "es9p_errors.h" #include #include #include #include static const char *lpa_header[] = { "User-Agent: gsma-rsp-lpad", "X-Admin-Protocol: gsma/rsp/v2.2.0", "Content-Type: application/json", NULL, }; static void es9p_base64_trim(char *str) { char *p = str; while (*p) { if (*p == '\n' || *p == '\r' || *p == ' ' || *p == '\t') { memmove(p, p + 1, strlen(p)); } else { p++; } } } static int es9p_trans_ex(struct euicc_ctx *ctx, const char *url, const char *url_postfix, uint32_t *rcode, char **str_rx, const char *str_tx) { int fret = 0; uint32_t rcode_mearged; uint8_t *rbuf = NULL; uint32_t rlen; char *full_url = NULL; const char *url_prefix = "https://"; if (!ctx->http.interface) { goto err; } full_url = malloc(strlen(url_prefix) + strlen(url) + strlen(url_postfix) + 1); if (full_url == NULL) { goto err; } full_url[0] = '\0'; strcat(full_url, url_prefix); strcat(full_url, url); strcat(full_url, url_postfix); if (getenv("LIBEUICC_DEBUG_HTTP")) { fprintf(stderr, "[DEBUG] [HTTP] [TX] url: %s, data: %s\n", full_url, str_tx); } if (ctx->http.interface->transmit(ctx, full_url, &rcode_mearged, &rbuf, &rlen, (const uint8_t *)str_tx, strlen(str_tx), lpa_header) < 0) { goto err; } if (getenv("LIBEUICC_DEBUG_HTTP")) { fprintf(stderr, "[DEBUG] [HTTP] [RX] rcode: %d, data: %s\n", rcode_mearged, rbuf); } free(full_url); full_url = NULL; *str_rx = malloc(rlen + 1); if (*str_rx == NULL) { goto err; } memcpy(*str_rx, rbuf, rlen); (*str_rx)[rlen] = '\0'; free(rbuf); rbuf = NULL; *rcode = rcode_mearged; fret = 0; goto exit; err: fret = -1; exit: free(full_url); free(rbuf); return fret; } static int es9p_trans_json(struct euicc_ctx *ctx, const char *smdp, const char *api, const char *ikey[], const char *idata[], const char *okey[], const char *oobj, void **optr[]) { int fret = 0; cJSON *sjroot = NULL; char *sbuf = NULL; uint32_t rcode; char *rbuf = NULL; cJSON *rjroot = NULL, *rjheader = NULL, *rjfunctionExecutionStatus = NULL; strncpy(ctx->http.status.reasonCode, "0.0.0", sizeof(ctx->http.status.reasonCode)); strncpy(ctx->http.status.subjectCode, "0.0.0", sizeof(ctx->http.status.subjectCode)); strncpy(ctx->http.status.subjectIdentifier, "unknown", sizeof(ctx->http.status.subjectIdentifier)); strncpy(ctx->http.status.message, "unknown", sizeof(ctx->http.status.message)); if (!(sjroot = cJSON_CreateObject())) { goto err; } for (int i = 0; ikey[i] != NULL; i++) { if (!cJSON_AddStringOrNullToObject(sjroot, ikey[i], idata[i])) { goto err; } } if (!(sbuf = cJSON_PrintUnformatted(sjroot))) { goto err; } cJSON_Delete(sjroot); sjroot = NULL; if (es9p_trans_ex(ctx, smdp, api, &rcode, &rbuf, sbuf) < 0) { strncpy(ctx->http.status.reasonCode, "0.0.0", sizeof(ctx->http.status.reasonCode)); strncpy(ctx->http.status.subjectCode, "0.0.0", sizeof(ctx->http.status.subjectCode)); strncpy(ctx->http.status.subjectIdentifier, "unknown", sizeof(ctx->http.status.subjectIdentifier)); strncpy(ctx->http.status.message, "HTTP transport failed", sizeof(ctx->http.status.message)); goto err; } free(sbuf); sbuf = NULL; if (rcode / 100 != 2) { strncpy(ctx->http.status.reasonCode, "0.0.0", sizeof(ctx->http.status.reasonCode)); strncpy(ctx->http.status.subjectCode, "0.0.0", sizeof(ctx->http.status.subjectCode)); snprintf(ctx->http.status.subjectIdentifier, sizeof(ctx->http.status.subjectIdentifier), "%d", rcode); strncpy(ctx->http.status.message, "HTTP status code error", sizeof(ctx->http.status.message)); goto err; } if (!okey) { fret = 0; goto exit; } if (!(rjroot = cJSON_Parse((const char *)rbuf))) { strncpy(ctx->http.status.reasonCode, "0.0.0", sizeof(ctx->http.status.reasonCode)); strncpy(ctx->http.status.subjectCode, "0.0.0", sizeof(ctx->http.status.subjectCode)); strncpy(ctx->http.status.subjectIdentifier, "root", sizeof(ctx->http.status.subjectIdentifier)); strncpy(ctx->http.status.message, "Not JSON", sizeof(ctx->http.status.message)); goto err; } free(rbuf); rbuf = NULL; if (!cJSON_IsObject(rjroot)) { strncpy(ctx->http.status.reasonCode, "0.0.0", sizeof(ctx->http.status.reasonCode)); strncpy(ctx->http.status.subjectCode, "0.0.0", sizeof(ctx->http.status.subjectCode)); strncpy(ctx->http.status.subjectIdentifier, "root", sizeof(ctx->http.status.subjectIdentifier)); strncpy(ctx->http.status.message, "Not Object", sizeof(ctx->http.status.message)); goto err; } if (!cJSON_HasObjectItem(rjroot, "header")) { strncpy(ctx->http.status.reasonCode, "0.0.0", sizeof(ctx->http.status.reasonCode)); strncpy(ctx->http.status.subjectCode, "0.0.0", sizeof(ctx->http.status.subjectCode)); strncpy(ctx->http.status.subjectIdentifier, "header", sizeof(ctx->http.status.subjectIdentifier)); strncpy(ctx->http.status.message, "Critical object missing", sizeof(ctx->http.status.message)); goto err; } rjheader = cJSON_GetObjectItem(rjroot, "header"); if (!cJSON_HasObjectItem(rjheader, "functionExecutionStatus")) { strncpy(ctx->http.status.reasonCode, "0.0.0", sizeof(ctx->http.status.reasonCode)); strncpy(ctx->http.status.subjectCode, "0.0.0", sizeof(ctx->http.status.subjectCode)); strncpy(ctx->http.status.subjectIdentifier, "functionExecutionStatus", sizeof(ctx->http.status.subjectIdentifier)); strncpy(ctx->http.status.message, "Critical object missing", sizeof(ctx->http.status.message)); goto err; } rjfunctionExecutionStatus = cJSON_GetObjectItem(rjheader, "functionExecutionStatus"); if (cJSON_HasObjectItem(rjfunctionExecutionStatus, "statusCodeData")) { cJSON *statusCodeData = cJSON_GetObjectItem(rjfunctionExecutionStatus, "statusCodeData"); if (cJSON_HasObjectItem(statusCodeData, "reasonCode") && cJSON_IsString(cJSON_GetObjectItem(statusCodeData, "reasonCode"))) { strncpy(ctx->http.status.reasonCode, cJSON_GetObjectItem(statusCodeData, "reasonCode")->valuestring, sizeof(ctx->http.status.reasonCode)); } if (cJSON_HasObjectItem(statusCodeData, "subjectCode") && cJSON_IsString(cJSON_GetObjectItem(statusCodeData, "subjectCode"))) { strncpy(ctx->http.status.subjectCode, cJSON_GetObjectItem(statusCodeData, "subjectCode")->valuestring, sizeof(ctx->http.status.subjectCode)); } if (cJSON_HasObjectItem(statusCodeData, "subjectIdentifier") && cJSON_IsString(cJSON_GetObjectItem(statusCodeData, "subjectIdentifier"))) { strncpy(ctx->http.status.subjectIdentifier, cJSON_GetObjectItem(statusCodeData, "subjectIdentifier")->valuestring, sizeof(ctx->http.status.subjectIdentifier)); } if (cJSON_HasObjectItem(statusCodeData, "message") && cJSON_IsString(cJSON_GetObjectItem(statusCodeData, "message"))) { strncpy(ctx->http.status.message, cJSON_GetObjectItem(statusCodeData, "message")->valuestring, sizeof(ctx->http.status.message)); } else { const char *message = es9p_error_message(ctx->http.status.subjectCode, ctx->http.status.reasonCode); if (message != NULL) { strncpy(ctx->http.status.message, message, sizeof(ctx->http.status.message)); } else { snprintf(ctx->http.status.message, sizeof(ctx->http.status.message), "subject-code: %s, reason-code: %s", ctx->http.status.subjectCode, ctx->http.status.reasonCode); } } } for (int i = 0; okey[i] != NULL; i++) { cJSON *obj; obj = cJSON_GetObjectItem(rjroot, okey[i]); if (!obj) { goto err; } if (cJSON_IsString(obj)) { if (!(*optr[i] = strdup(obj->valuestring))) { goto err; } } else { if (oobj[i] == 0) { goto err; } if (!(*(optr[i]) = cJSON_Duplicate(obj, 1))) { goto err; } } } cJSON_Delete(rjroot); rjroot = NULL; fret = 0; goto exit; err: fret = -1; exit: free(sbuf); cJSON_Delete(sjroot); free(rbuf); cJSON_Delete(rjroot); return fret; } int es9p_initiate_authentication_r(struct euicc_ctx *ctx, char **transaction_id, struct es10b_authenticate_server_param *resp, const char *server_address, const char *b64_euicc_challenge, const char *b64_euicc_info_1) { const char *ikey[] = {"smdpAddress", "euiccChallenge", "euiccInfo1", NULL}; const char *idata[] = {ctx->http.server_address, b64_euicc_challenge, b64_euicc_info_1, NULL}; const char *okey[] = {"transactionId", "serverSigned1", "serverSignature1", "euiccCiPKIdToBeUsed", "serverCertificate", NULL}; const char oobj[] = {0, 0, 0, 0, 0}; void **optr[] = {(void **)transaction_id, (void **)&resp->b64_serverSigned1, (void **)&resp->b64_serverSignature1, (void **)&resp->b64_euiccCiPKIdToBeUsed, (void **)&resp->b64_serverCertificate, NULL}; if (es9p_trans_json(ctx, ctx->http.server_address, "/gsma/rsp2/es9plus/initiateAuthentication", ikey, idata, okey, oobj, optr)) { return -1; } es9p_base64_trim(resp->b64_serverSigned1); es9p_base64_trim(resp->b64_serverSignature1); es9p_base64_trim(resp->b64_euiccCiPKIdToBeUsed); es9p_base64_trim(resp->b64_serverCertificate); return 0; } int es9p_get_bound_profile_package_r(struct euicc_ctx *ctx, char **b64_bound_profile_package, const char *server_address, const char *transaction_id, const char *b64_prepare_download_response) { const char *ikey[] = {"transactionId", "prepareDownloadResponse", NULL}; const char *idata[] = {transaction_id, b64_prepare_download_response, NULL}; const char *okey[] = {"boundProfilePackage", NULL}; const char oobj[] = {0}; void **optr[] = {(void **)b64_bound_profile_package, NULL}; if (es9p_trans_json(ctx, ctx->http.server_address, "/gsma/rsp2/es9plus/getBoundProfilePackage", ikey, idata, okey, oobj, optr)) { return -1; } es9p_base64_trim(*b64_bound_profile_package); return 0; } int es9p_authenticate_client_r(struct euicc_ctx *ctx, struct es10b_prepare_download_param *resp, const char *server_address, const char *transaction_id, const char *b64_authenticate_server_response) { const char *ikey[] = {"transactionId", "authenticateServerResponse", NULL}; const char *idata[] = {transaction_id, b64_authenticate_server_response, NULL}; const char *okey[] = {"profileMetadata", "smdpSigned2", "smdpSignature2", "smdpCertificate", NULL}; const char oobj[] = {0, 0, 0, 0}; void **optr[] = {(void **)&resp->b64_profileMetadata, (void **)&resp->b64_smdpSigned2, (void **)&resp->b64_smdpSignature2, (void **)&resp->b64_smdpCertificate, NULL}; if (es9p_trans_json(ctx, ctx->http.server_address, "/gsma/rsp2/es9plus/authenticateClient", ikey, idata, okey, oobj, optr)) { return -1; } es9p_base64_trim(resp->b64_profileMetadata); es9p_base64_trim(resp->b64_smdpSigned2); es9p_base64_trim(resp->b64_smdpSignature2); es9p_base64_trim(resp->b64_smdpCertificate); return 0; } int es9p_cancel_session_r(struct euicc_ctx *ctx, const char *server_address, const char *transaction_id, const char *b64_cancel_session_response) { const char *ikey[] = {"transactionId", "cancelSessionResponse", NULL}; const char *idata[] = {transaction_id, b64_cancel_session_response, NULL}; if (es9p_trans_json(ctx, ctx->http.server_address, "/gsma/rsp2/es9plus/cancelSession", ikey, idata, NULL, NULL, NULL)) { return -1; } return 0; } int es11_authenticate_client_r(struct euicc_ctx *ctx, char ***smdp_list, const char *server_address, const char *transaction_id, const char *b64_authenticate_server_response) { int fret = 0; cJSON *j_eventEntries = NULL; int j_eventEntries_size = 0; const char *ikey[] = {"transactionId", "authenticateServerResponse", NULL}; const char *idata[] = {transaction_id, b64_authenticate_server_response, NULL}; const char *okey[] = {"eventEntries", NULL}; const char oobj[] = {1}; void **optr[] = {(void **)&j_eventEntries, NULL}; if (es9p_trans_json(ctx, ctx->http.server_address, "/gsma/rsp2/es9plus/authenticateClient", ikey, idata, okey, oobj, optr)) { return -1; } if (j_eventEntries == NULL || !cJSON_IsArray(j_eventEntries)) { return -1; } j_eventEntries_size = cJSON_GetArraySize(j_eventEntries); *smdp_list = malloc(sizeof(char *) * (j_eventEntries_size + 1)); if (*smdp_list == NULL) { fret = -1; goto err; } memset(*smdp_list, 0, sizeof(char *) * (j_eventEntries_size + 1)); for (int i = 0; i < j_eventEntries_size; i++) { cJSON *j_event = cJSON_GetArrayItem(j_eventEntries, i); cJSON *j_eventType = cJSON_GetObjectItem(j_event, "rspServerAddress"); if (j_eventType == NULL || !cJSON_IsString(j_eventType)) { fret = -1; goto err; } (*smdp_list)[i] = strdup(j_eventType->valuestring); } fret = 0; goto exit; err: if (*smdp_list) { for (int i = 0; i < j_eventEntries_size; i++) { free((*smdp_list)[i]); } free(*smdp_list); *smdp_list = NULL; } exit: cJSON_Delete(j_eventEntries); return fret; } int es9p_initiate_authentication(struct euicc_ctx *ctx) { int fret; if (ctx->http._internal.authenticate_server_param) { return -1; } if (ctx->http._internal.b64_euicc_challenge == NULL) { return -1; } if (ctx->http._internal.b64_euicc_info_1 == NULL) { return -1; } ctx->http._internal.authenticate_server_param = malloc(sizeof(struct es10b_authenticate_server_param)); if (ctx->http._internal.authenticate_server_param == NULL) { return -1; } fret = es9p_initiate_authentication_r( ctx, &ctx->http._internal.transaction_id_http, ctx->http._internal.authenticate_server_param, ctx->http.server_address, ctx->http._internal.b64_euicc_challenge, ctx->http._internal.b64_euicc_info_1); if (fret < 0) { free(ctx->http._internal.authenticate_server_param); ctx->http._internal.authenticate_server_param = NULL; return fret; } free(ctx->http._internal.b64_euicc_challenge); ctx->http._internal.b64_euicc_challenge = NULL; free(ctx->http._internal.b64_euicc_info_1); ctx->http._internal.b64_euicc_info_1 = NULL; return fret; } int es9p_get_bound_profile_package(struct euicc_ctx *ctx) { int fret; if (ctx->http._internal.b64_bound_profile_package) { return -1; } if (ctx->http._internal.b64_prepare_download_response == NULL) { return -1; } fret = es9p_get_bound_profile_package_r(ctx, &ctx->http._internal.b64_bound_profile_package, ctx->http.server_address, ctx->http._internal.transaction_id_http, ctx->http._internal.b64_prepare_download_response); if (fret < 0) { free(ctx->http._internal.b64_bound_profile_package); ctx->http._internal.b64_bound_profile_package = NULL; return fret; } free(ctx->http._internal.b64_prepare_download_response); ctx->http._internal.b64_prepare_download_response = NULL; return fret; } int es9p_authenticate_client(struct euicc_ctx *ctx) { int fret; if (ctx->http._internal.prepare_download_param) { return -1; } if (ctx->http._internal.b64_authenticate_server_response == NULL) { return -1; } ctx->http._internal.prepare_download_param = malloc(sizeof(struct es10b_prepare_download_param)); if (ctx->http._internal.prepare_download_param == NULL) { return -1; } fret = es9p_authenticate_client_r(ctx, ctx->http._internal.prepare_download_param, ctx->http.server_address, ctx->http._internal.transaction_id_http, ctx->http._internal.b64_authenticate_server_response); if (fret < 0) { free(ctx->http._internal.prepare_download_param); ctx->http._internal.prepare_download_param = NULL; return fret; } free(ctx->http._internal.b64_authenticate_server_response); ctx->http._internal.b64_authenticate_server_response = NULL; return fret; } int es9p_cancel_session(struct euicc_ctx *ctx) { int fret; if (ctx->http._internal.b64_cancel_session_response == NULL) { return -1; } fret = es9p_cancel_session_r(ctx, ctx->http.server_address, ctx->http._internal.transaction_id_http, ctx->http._internal.b64_cancel_session_response); if (fret < 0) { return fret; } free(ctx->http._internal.b64_cancel_session_response); ctx->http._internal.b64_cancel_session_response = NULL; return fret; } int es11_authenticate_client(struct euicc_ctx *ctx, char ***smdp_list) { int fret; if (ctx->http._internal.b64_authenticate_server_response == NULL) { return -1; } fret = es11_authenticate_client_r(ctx, smdp_list, ctx->http.server_address, ctx->http._internal.transaction_id_http, ctx->http._internal.b64_authenticate_server_response); if (fret < 0) { return fret; } free(ctx->http._internal.b64_authenticate_server_response); ctx->http._internal.b64_authenticate_server_response = NULL; return fret; } int es9p_handle_notification(struct euicc_ctx *ctx, const char *b64_PendingNotification) { const char *ikey[] = {"pendingNotification", NULL}; const char *idata[] = {b64_PendingNotification, NULL}; return es9p_trans_json(ctx, ctx->http.server_address, "/gsma/rsp2/es9plus/handleNotification", ikey, idata, NULL, NULL, NULL); } void es11_smdp_list_free_all(char **smdp_list) { if (smdp_list) { for (int i = 0; smdp_list[i] != NULL; i++) { free(smdp_list[i]); } free(smdp_list); } } estkme-group-lpac-c2fcf5e/euicc/es9p.h000066400000000000000000000032511504765665400177740ustar00rootroot00000000000000#pragma once #include "es10b.h" #include "euicc.h" #include int es9p_initiate_authentication_r(struct euicc_ctx *ctx, char **transaction_id, struct es10b_authenticate_server_param *resp, const char *server_address, const char *b64_euicc_challenge, const char *b64_euicc_info_1); int es9p_get_bound_profile_package_r(struct euicc_ctx *ctx, char **b64_bound_profile_package, const char *server_address, const char *transaction_id, const char *b64_prepare_download_response); int es9p_authenticate_client_r(struct euicc_ctx *ctx, struct es10b_prepare_download_param *resp, const char *server_address, const char *transaction_id, const char *b64_authenticate_server_response); int es9p_cancel_session_r(struct euicc_ctx *ctx, const char *server_address, const char *transaction_id, const char *b64_cancel_session_response); int es9p_initiate_authentication(struct euicc_ctx *ctx); int es9p_get_bound_profile_package(struct euicc_ctx *ctx); int es9p_authenticate_client(struct euicc_ctx *ctx); int es9p_cancel_session(struct euicc_ctx *ctx); int es11_authenticate_client_r(struct euicc_ctx *ctx, char ***smdp_list, const char *server_address, const char *transaction_id, const char *b64_authenticate_server_response); int es11_authenticate_client(struct euicc_ctx *ctx, char ***smdp_list); int es9p_handle_notification(struct euicc_ctx *ctx, const char *b64_PendingNotification); void es11_smdp_list_free_all(char **smdp_list); estkme-group-lpac-c2fcf5e/euicc/es9p_errors.c000066400000000000000000000104561504765665400213700ustar00rootroot00000000000000#include "es9p_errors.h" #include struct es9p_error { const char *subject_code; const char *reason_code; const char *description; }; // clang-format off static const struct es9p_error es9p_errors[] = { {"8.1", "4.8", "eUICC does not have sufficient space for this Profile"}, {"8.1", "6.1", "eUICC signature is invalid or serverChallenge is invalid"}, {"8.1.1", "2.2", "Indicates that the EID is missing in the context of this order (SM-DS address provided or MatchingID value is empty)"}, {"8.1.1", "3.1", "Indicates that a different EID is already associated with this ICCID"}, {"8.1.1", "3.8", "EID doesn't match the expected value"}, {"8.1.2", "6.1", "EUM Certificate is invalid"}, {"8.1.2", "6.3", "EUM Certificate has expired"}, {"8.1.3", "6.1", "eUICC Certificate is invalid"}, {"8.1.3", "6.3", "eUICC Certificate has expired"}, {"8.2", "1.2", "Profile has not yet been released"}, {"8.2", "3.7", "BPP is not available for a new binding"}, {"8.2.1", "1.2", "Indicates that the function caller is not allowed to perform this function on the target Profile"}, {"8.2.1", "3.1", "Indicates that a different EID is associated with this ICCID"}, {"8.2.1", "3.3", "Indicates that the Profile identified by the provided ICCID is not available"}, {"8.2.1", "3.5", "Indicates that the target Profile cannot be released"}, {"8.2.1", "3.9", "Indicates that the Profile Type identified by this Profile Type is unknown to the SM-DP+"}, {"8.2.5", "1.2", "Indicates that the function caller is not allowed to perform this function on the Profile Type"}, {"8.2.5", "3.7", "No more Profile available for the requested Profile Type"}, {"8.2.5", "3.8", "Indicates that the Profile Type identified by this Profile Type is not aligned with the Profile Type of Profile identified by the ICCID"}, {"8.2.5", "3.9", "Indicates that the Profile Type identified by this Profile Type is unknown to the SM-DP+"}, {"8.2.5", "4.3", "No eligible Profile for this eUICC/Device"}, {"8.2.6", "3.1", "Indicates that a different MatchingID is associated with this ICCID"}, {"8.2.6", "3.3", "Conflicting MatchingID value"}, {"8.2.6", "3.8", "MatchingID (AC_Token or EventID) is refused"}, {"8.2.7", "2.2", "Confirmation Code is missing"}, {"8.2.7", "3.8", "Confirmation Code is refused"}, {"8.2.7", "6.4", "The maximum number of retries for the Confirmation Code has been exceeded"}, {"8.8", "3.1", "The provided SM-DP+ OID is invalid"}, {"8.8.1", "3.8", "Invalid SM-DP+ Address"}, {"8.8.2", "3.1", "None of the proposed Public Key Identifiers is supported by the SM-DP+"}, {"8.8.3", "3.1", "The Specification Version Number indicated by the eUICC is not supported by the SM-DP+"}, {"8.8.4", "3.7", "The SM-DP+ has no CERT.DPauth.ECDSA signed by one of the CI Public Key supported by the eUICC"}, {"8.8.5", "4.1", "The Download order has expired"}, {"8.8.5", "6.4", "The maximum number of retries for the Profile download order has been exceeded"}, {"8.9", "4.2", "Root SM-DS has raised an error"}, {"8.9", "5.1", "Root SM-DS was unavailable"}, {"8.9.1", "3.8", "Invalid SM-DS Address"}, {"8.9.2", "3.1", "None of the proposed Public Key Identifiers is supported by the SM-DS"}, {"8.9.3", "3.1", "The Specification Version Number indicated by the eUICC is not supported by the SM-DS"}, {"8.9.4", "3.7", "The SM-DS has no CERT.DS.ECDSA signed by one of the GSMA CI Public Key supported by the eUICC"}, {"8.9.5", "3.3", "The Event Record already exist in the SM-DS (EventID duplicated)"}, {"8.9.5", "3.9", "No Event identified by the Event ID for the EID exists"}, {"8.10.1", "3.9", "The RSP session identified by the TransactionID is unknown"}, {"8.11.1", "3.9", "Unknown CI Public Key. The CI used by the EUM Certificate is not a trusted root."}, }; // clang-format on const char *es9p_error_message(const char *subject_code, const char *reason_code) { struct es9p_error error; for (int i = 0; i < sizeof(es9p_errors) / sizeof(es9p_errors[0]); i++) { error = es9p_errors[i]; if (strcmp(error.subject_code, subject_code) == 0 && strcmp(error.reason_code, reason_code) == 0) { return error.description; } } return NULL; } estkme-group-lpac-c2fcf5e/euicc/es9p_errors.h000066400000000000000000000001411504765665400213630ustar00rootroot00000000000000#pragma once const char *es9p_error_message(const char *subject_code, const char *reason_code); estkme-group-lpac-c2fcf5e/euicc/euicc.c000066400000000000000000000140611504765665400202000ustar00rootroot00000000000000#include "euicc.private.h" #include "hexutil.h" #include #include #include #include #include #define ISD_R_AID "\xA0\x00\x00\x05\x59\x10\x10\xFF\xFF\xFF\xFF\x89\x00\x00\x01\x00" #define APDU_EUICC_HEADER 0x80, 0xE2 #define APDU_CONTINUE_READ_HEADER 0x80, 0xC0, 0x00, 0x00 static int es10x_transmit(struct euicc_ctx *ctx, struct apdu_response *response, struct apdu_request *req, unsigned req_len) { req->cla = (req->cla & 0xF0) | (ctx->apdu._internal.logic_channel & 0x0F); return euicc_apdu_transmit(ctx, response, req, req_len); } static int es10x_transmit_iter(struct euicc_ctx *ctx, struct apdu_request *req, unsigned req_len, int (*callback)(struct apdu_response *response, void *userdata), void *userdata) { struct apdu_request *request = NULL; struct apdu_response response; if (es10x_transmit(ctx, &response, req, req_len) < 0) { return -1; } do { if (response.length > 0) { if (callback(&response, userdata) < 0) { return -1; } } euicc_apdu_response_free(&response); if (response.sw1 == SW1_LAST) { int ret; if ((ret = euicc_apdu_le(ctx, &request, APDU_CONTINUE_READ_HEADER, response.sw2)) < 0) { return -1; } if (es10x_transmit(ctx, &response, request, ret) < 0) { return -1; } continue; } else if ((response.sw1 & 0xF0) == SW1_OK) { return 0; } return -1; } while (1); } int es10x_command_buildrequest(struct euicc_ctx *ctx, struct apdu_request **request, uint8_t p1, uint8_t p2, const uint8_t *der_req, unsigned req_len) { int ret; ret = euicc_apdu_lc(ctx, request, APDU_EUICC_HEADER, p1, p2, req_len); if (ret < 0) return ret; memcpy((*request)->data, der_req, req_len); return ret; } static int es10x_command_buildrequest_continue(struct euicc_ctx *ctx, uint8_t reqseq, struct apdu_request **request, const uint8_t *der_req, unsigned req_len) { return es10x_command_buildrequest(ctx, request, 0x11, reqseq, der_req, req_len); } static int es10x_command_buildrequest_last(struct euicc_ctx *ctx, uint8_t reqseq, struct apdu_request **request, const uint8_t *der_req, unsigned req_len) { return es10x_command_buildrequest(ctx, request, 0x91, reqseq, der_req, req_len); } int es10x_command_iter(struct euicc_ctx *ctx, const uint8_t *der_req, unsigned req_len, int (*callback)(struct apdu_response *response, void *userdata), void *userdata) { int ret, reqseq; struct apdu_request *req; const uint8_t *req_ptr; reqseq = 0; req_ptr = der_req; while (req_len) { uint8_t rlen; if (req_len > ctx->es10x_mss) { rlen = ctx->es10x_mss; ret = es10x_command_buildrequest_continue(ctx, reqseq, &req, req_ptr, rlen); } else { rlen = req_len; ret = es10x_command_buildrequest_last(ctx, reqseq, &req, req_ptr, rlen); } req_len -= rlen; if (ret < 0) return -1; ret = es10x_transmit_iter(ctx, req, ret, callback, userdata); if (ret < 0) return -1; req_ptr += rlen; reqseq++; } return 0; } struct userdata_es10x_command { uint8_t *resp; unsigned resp_len; }; static int iter_es10x_command(struct apdu_response *response, void *userdata) { struct userdata_es10x_command *ud = (struct userdata_es10x_command *)userdata; uint8_t *new_response_data; new_response_data = realloc(ud->resp, ud->resp_len + response->length); if (!new_response_data) { return -1; } ud->resp = new_response_data; memcpy(ud->resp + ud->resp_len, response->data, response->length); ud->resp_len += response->length; return 0; } int es10x_command(struct euicc_ctx *ctx, uint8_t **resp, unsigned *resp_len, const uint8_t *der_req, unsigned req_len) { int ret = 0; struct userdata_es10x_command ud; *resp = NULL; *resp_len = 0; memset(&ud, 0, sizeof(ud)); ret = es10x_command_iter(ctx, der_req, req_len, iter_es10x_command, &ud); if (ret < 0) { free(ud.resp); return -1; } *resp = ud.resp; *resp_len = ud.resp_len; return 0; } int euicc_init(struct euicc_ctx *ctx) { int ret; if (ctx->aid == NULL) { ctx->aid = (const uint8_t *)ISD_R_AID; ctx->aid_len = sizeof(ISD_R_AID) - 1; } if (ctx->es10x_mss == 0) { ctx->es10x_mss = 120; } ret = ctx->apdu.interface->connect(ctx); if (ret < 0) { return -1; } ret = ctx->apdu.interface->logic_channel_open(ctx, ctx->aid, ctx->aid_len); if (ret < 0) { ctx->apdu.interface->disconnect(ctx); return -1; } ctx->apdu._internal.logic_channel = ret; return 0; } void euicc_fini(struct euicc_ctx *ctx) { ctx->apdu.interface->logic_channel_close(ctx, ctx->apdu._internal.logic_channel); ctx->apdu.interface->disconnect(ctx); ctx->apdu._internal.logic_channel = 0; } void euicc_http_cleanup(struct euicc_ctx *ctx) { free(ctx->http._internal.transaction_id_http); free(ctx->http._internal.transaction_id_bin); free(ctx->http._internal.b64_euicc_challenge); free(ctx->http._internal.b64_euicc_info_1); es10b_authenticate_server_param_free(ctx->http._internal.authenticate_server_param); free(ctx->http._internal.authenticate_server_param); free(ctx->http._internal.b64_authenticate_server_response); es10b_prepare_download_param_free(ctx->http._internal.prepare_download_param); free(ctx->http._internal.prepare_download_param); free(ctx->http._internal.b64_prepare_download_response); free(ctx->http._internal.b64_bound_profile_package); free(ctx->http._internal.b64_cancel_session_response); memset(&ctx->http._internal, 0, sizeof(ctx->http._internal)); } estkme-group-lpac-c2fcf5e/euicc/euicc.h000066400000000000000000000030141504765665400202010ustar00rootroot00000000000000#pragma once #include "es10b.h" #include "interface.h" #include #ifdef interface # undef interface #endif struct euicc_ctx { const uint8_t *aid; uint8_t aid_len; uint8_t es10x_mss; struct { const struct euicc_apdu_interface *interface; struct { int logic_channel; struct { uint8_t apdu_header[5]; uint8_t body[255]; } __attribute__((packed)) request_buffer; } _internal; } apdu; struct { const struct euicc_http_interface *interface; const char *server_address; struct { char subjectCode[8 + 1]; char reasonCode[8 + 1]; char subjectIdentifier[128 + 1]; char message[128 + 1]; } status; struct { char *transaction_id_http; uint8_t *transaction_id_bin; uint32_t transaction_id_bin_len; char *b64_euicc_challenge; char *b64_euicc_info_1; struct es10b_authenticate_server_param *authenticate_server_param; char *b64_authenticate_server_response; struct es10b_prepare_download_param *prepare_download_param; char *b64_prepare_download_response; char *b64_bound_profile_package; char *b64_cancel_session_response; } _internal; } http; void *userdata; }; int euicc_init(struct euicc_ctx *ctx); void euicc_fini(struct euicc_ctx *ctx); void euicc_http_cleanup(struct euicc_ctx *ctx); estkme-group-lpac-c2fcf5e/euicc/euicc.private.h000066400000000000000000000005721504765665400216600ustar00rootroot00000000000000#pragma once #include "euicc.h" #include "interface.private.h" int es10x_command_iter(struct euicc_ctx *ctx, const uint8_t *der_req, unsigned req_len, int (*callback)(struct apdu_response *response, void *userdata), void *userdata); int es10x_command(struct euicc_ctx *ctx, uint8_t **resp, unsigned *resp_len, const uint8_t *der_req, unsigned req_len); estkme-group-lpac-c2fcf5e/euicc/hexutil.c000066400000000000000000000067251504765665400206020ustar00rootroot00000000000000#include "hexutil.h" #include #include #include int euicc_hexutil_bin2hex(char *output, uint32_t output_len, const uint8_t *bin, uint32_t bin_len) { const char hexDigits[] = "0123456789abcdef"; if (!bin || !output) { return -1; } if (output_len < 2 * bin_len + 1) { return -1; } for (uint32_t i = 0; i < bin_len; ++i) { char byte = bin[i]; output[2 * i] = hexDigits[(byte >> 4) & 0x0F]; output[2 * i + 1] = hexDigits[byte & 0x0F]; } output[2 * bin_len] = '\0'; return 0; } int euicc_hexutil_hex2bin(uint8_t *output, uint32_t output_len, const char *str) { return euicc_hexutil_hex2bin_r(output, output_len, str, strlen(str)); } int euicc_hexutil_hex2bin_r(uint8_t *output, uint32_t output_len, const char *str, uint32_t str_len) { uint32_t length; if (!str || !output || str_len % 2 != 0) { return -1; } length = str_len / 2; if (length > output_len) { return -1; } for (uint32_t i = 0; i < length; ++i) { char high = str[2 * i]; char low = str[2 * i + 1]; if (high >= '0' && high <= '9') { high -= '0'; } else if (high >= 'a' && high <= 'f') { high = high - 'a' + 10; } else if (high >= 'A' && high <= 'F') { high = high - 'A' + 10; } else { return -1; } if (low >= '0' && low <= '9') { low -= '0'; } else if (low >= 'a' && low <= 'f') { low = low - 'a' + 10; } else if (low >= 'A' && low <= 'F') { low = low - 'A' + 10; } else { return -1; } output[i] = (high << 4) + low; } return length; } int euicc_hexutil_gsmbcd2bin(uint8_t *output, uint32_t output_len, const char *str, uint32_t padding_to) { uint32_t str_length; uint32_t idx = 0; str_length = strlen(str); if (output_len < (str_length + 1) / 2) { return -1; } if (output_len < padding_to) { return -1; } for (uint32_t i = 0; i < str_length; i += 2) { char high_nibble = (i + 1 < str_length) ? str[i + 1] : 'F'; char low_nibble = str[i]; uint8_t high_nibble_val = 0x0; uint8_t low_nibble_val = 0x0; if (low_nibble >= '0' && low_nibble <= '9') { low_nibble_val = low_nibble - '0'; } else if (low_nibble == 'F' || low_nibble == 'f') { low_nibble_val = 0x0F; } else { return -1; } if (high_nibble >= '0' && high_nibble <= '9') { high_nibble_val = high_nibble - '0'; } else if (high_nibble == 'F' || high_nibble == 'f') { high_nibble_val = 0xF; } else { return -1; } output[idx] = (high_nibble_val << 4) | low_nibble_val; idx++; } for (; idx < padding_to; idx++) { output[idx] = 0xFF; } return idx; } int euicc_hexutil_bin2gsmbcd(char *output, uint32_t output_len, const uint8_t *binData, uint32_t length) { if (euicc_hexutil_bin2hex(output, output_len, binData, length)) { return -1; } length = strlen(output); for (int i = 0; i < length - 1; i += 2) { char temp = output[i]; output[i] = output[i + 1]; output[i + 1] = temp; } for (int i = length - 1; i >= 0; i--) { if (output[i] != 'f') { break; } output[i] = '\0'; } return 0; } estkme-group-lpac-c2fcf5e/euicc/hexutil.h000066400000000000000000000010251504765665400205730ustar00rootroot00000000000000#pragma once #include int euicc_hexutil_hex2bin_r(uint8_t *output, uint32_t output_len, const char *str, uint32_t str_len); int euicc_hexutil_hex2bin(uint8_t *output, uint32_t output_len, const char *str); int euicc_hexutil_bin2hex(char *output, uint32_t output_len, const uint8_t *bin, uint32_t bin_len); int euicc_hexutil_gsmbcd2bin(uint8_t *output, uint32_t output_len, const char *str, uint32_t padding_to); int euicc_hexutil_bin2gsmbcd(char *output, uint32_t output_len, const uint8_t *binData, uint32_t length); estkme-group-lpac-c2fcf5e/euicc/interface.c000066400000000000000000000054561504765665400210600ustar00rootroot00000000000000#include "interface.private.h" #include #include #include static int lc(struct apdu_request *apdu, uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t datalen) { apdu->cla = cla; apdu->ins = ins; apdu->p1 = p1; apdu->p2 = p2; apdu->length = datalen; return datalen + sizeof(struct apdu_request); } static int le(struct apdu_request *apdu, uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t requestlen) { apdu->cla = cla; apdu->ins = ins; apdu->p1 = p1; apdu->p2 = p2; apdu->length = requestlen; return sizeof(struct apdu_request); } int euicc_apdu_lc(struct euicc_ctx *ctx, struct apdu_request **apdu, uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t datalen) { *apdu = (struct apdu_request *)&ctx->apdu._internal.request_buffer; return lc(*apdu, cla, ins, p1, p2, datalen); } int euicc_apdu_le(struct euicc_ctx *ctx, struct apdu_request **apdu, uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t requestlen) { *apdu = (struct apdu_request *)&ctx->apdu._internal.request_buffer; return le(*apdu, cla, ins, p1, p2, requestlen); } static void euicc_apdu_request_print(const struct apdu_request *req, uint32_t request_len) { fprintf(stderr, "[DEBUG] [APDU] [TX] CLA: %02X, INS: %02X, P1: %02X, P2: %02X, Lc: %02X, Data: ", req->cla, req->ins, req->p1, req->p2, req->length); for (uint32_t i = 0; i < (request_len - sizeof(struct apdu_request)); i++) fprintf(stderr, "%02X ", (req->data[i] & 0xFF)); fprintf(stderr, "\n"); } static void euicc_apdu_response_print(const struct apdu_response *resp) { fprintf(stderr, "[DEBUG] [APDU] [RX] SW1: %02X, SW2: %02X, Data: ", resp->sw1, resp->sw2); for (uint32_t i = 0; i < resp->length; i++) fprintf(stderr, "%02X ", (resp->data[i] & 0xFF)); fprintf(stderr, "\n"); } int euicc_apdu_transmit(struct euicc_ctx *ctx, struct apdu_response *response, const struct apdu_request *request, uint32_t request_len) { const struct euicc_apdu_interface *in = ctx->apdu.interface; memset(response, 0x00, sizeof(*response)); if (getenv("LIBEUICC_DEBUG_APDU")) { euicc_apdu_request_print(request, request_len); } if (in->transmit(ctx, &response->data, &response->length, (uint8_t *)request, request_len) < 0) return -1; if (response->length < 2) return -1; response->sw1 = response->data[response->length - 2]; response->sw2 = response->data[response->length - 1]; response->length -= 2; if (getenv("LIBEUICC_DEBUG_APDU")) { euicc_apdu_response_print(response); } return 0; } void euicc_apdu_response_free(struct apdu_response *resp) { free(resp->data); resp->data = NULL; resp->length = 0; } estkme-group-lpac-c2fcf5e/euicc/interface.h000066400000000000000000000013151504765665400210530ustar00rootroot00000000000000#pragma once #include struct euicc_ctx; struct euicc_apdu_interface { int (*connect)(struct euicc_ctx *ctx); void (*disconnect)(struct euicc_ctx *ctx); int (*logic_channel_open)(struct euicc_ctx *ctx, const uint8_t *aid, uint8_t aid_len); void (*logic_channel_close)(struct euicc_ctx *ctx, uint8_t channel); int (*transmit)(struct euicc_ctx *ctx, uint8_t **rx, uint32_t *rx_len, const uint8_t *tx, uint32_t tx_len); void *userdata; }; struct euicc_http_interface { int (*transmit)(struct euicc_ctx *ctx, const char *url, uint32_t *rcode, uint8_t **rx, uint32_t *rx_len, const uint8_t *tx, uint32_t tx_len, const char **headers); void *userdata; }; estkme-group-lpac-c2fcf5e/euicc/interface.private.h000066400000000000000000000016331504765665400225270ustar00rootroot00000000000000#pragma once #include "euicc.h" #include "interface.h" #include enum apdu_sw1 { SW1_OK = 0x90, SW1_LAST = 0x61, }; struct apdu_request { uint8_t cla; uint8_t ins; uint8_t p1; uint8_t p2; uint8_t length; uint8_t data[]; } __attribute__((packed)); struct apdu_response { uint8_t *data; uint32_t length; uint8_t sw1; uint8_t sw2; }; int euicc_apdu_lc(struct euicc_ctx *ctx, struct apdu_request **apdu, uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t datalen); int euicc_apdu_le(struct euicc_ctx *ctx, struct apdu_request **apdu, uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t requestlen); int euicc_apdu_transmit(struct euicc_ctx *ctx, struct apdu_response *response, const struct apdu_request *req, uint32_t req_len); void euicc_apdu_response_free(struct apdu_response *resp); estkme-group-lpac-c2fcf5e/euicc/libeuicc.pc.in000066400000000000000000000004011504765665400214450ustar00rootroot00000000000000prefix="@CMAKE_INSTALL_PREFIX@" exec_prefix="${prefix}" libdir="${prefix}/lib" includedir="${prefix}/include" Name: libeuicc Description: Library to manipulate eUICC (eSIM) cards Version: @PROJECT_VERSION@ Cflags: -I${includedir} Libs: -L${libdir} -leuicc estkme-group-lpac-c2fcf5e/euicc/sha256.c000066400000000000000000000133541504765665400201240ustar00rootroot00000000000000/********************************************************************* * Filename: sha256.c * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Implementation of the SHA-256 hashing algorithm. SHA-256 is one of the three algorithms in the SHA2 specification. The others, SHA-384 and SHA-512, are not offered in this implementation. Algorithm specification can be found here: * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf This implementation uses little endian byte order. *********************************************************************/ /*************************** HEADER FILES ***************************/ #include "sha256.h" #include #include /****************************** MACROS ******************************/ #define ROTLEFT(a, b) (((a) << (b)) | ((a) >> (32 - (b)))) #define ROTRIGHT(a, b) (((a) >> (b)) | ((a) << (32 - (b)))) #define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z))) #define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) #define EP0(x) (ROTRIGHT(x, 2) ^ ROTRIGHT(x, 13) ^ ROTRIGHT(x, 22)) #define EP1(x) (ROTRIGHT(x, 6) ^ ROTRIGHT(x, 11) ^ ROTRIGHT(x, 25)) #define SIG0(x) (ROTRIGHT(x, 7) ^ ROTRIGHT(x, 18) ^ ((x) >> 3)) #define SIG1(x) (ROTRIGHT(x, 17) ^ ROTRIGHT(x, 19) ^ ((x) >> 10)) /**************************** VARIABLES *****************************/ static const WORD k[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; /*********************** FUNCTION DEFINITIONS ***********************/ static void sha256_transform(EUICC_SHA256_CTX *ctx, const BYTE data[]) { WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; for (i = 0, j = 0; i < 16; ++i, j += 4) m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); for (; i < 64; ++i) m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; a = ctx->state[0]; b = ctx->state[1]; c = ctx->state[2]; d = ctx->state[3]; e = ctx->state[4]; f = ctx->state[5]; g = ctx->state[6]; h = ctx->state[7]; for (i = 0; i < 64; ++i) { t1 = h + EP1(e) + CH(e, f, g) + k[i] + m[i]; t2 = EP0(a) + MAJ(a, b, c); h = g; g = f; f = e; e = d + t1; d = c; c = b; b = a; a = t1 + t2; } ctx->state[0] += a; ctx->state[1] += b; ctx->state[2] += c; ctx->state[3] += d; ctx->state[4] += e; ctx->state[5] += f; ctx->state[6] += g; ctx->state[7] += h; } void euicc_sha256_init(EUICC_SHA256_CTX *ctx) { ctx->datalen = 0; ctx->bitlen = 0; ctx->state[0] = 0x6a09e667; ctx->state[1] = 0xbb67ae85; ctx->state[2] = 0x3c6ef372; ctx->state[3] = 0xa54ff53a; ctx->state[4] = 0x510e527f; ctx->state[5] = 0x9b05688c; ctx->state[6] = 0x1f83d9ab; ctx->state[7] = 0x5be0cd19; } void euicc_sha256_update(EUICC_SHA256_CTX *ctx, const BYTE data[], size_t len) { WORD i; for (i = 0; i < len; ++i) { ctx->data[ctx->datalen] = data[i]; ctx->datalen++; if (ctx->datalen == 64) { sha256_transform(ctx, ctx->data); ctx->bitlen += 512; ctx->datalen = 0; } } } void euicc_sha256_final(EUICC_SHA256_CTX *ctx, BYTE hash[]) { WORD i; i = ctx->datalen; // Pad whatever data is left in the buffer. if (ctx->datalen < 56) { ctx->data[i++] = 0x80; while (i < 56) ctx->data[i++] = 0x00; } else { ctx->data[i++] = 0x80; while (i < 64) ctx->data[i++] = 0x00; sha256_transform(ctx, ctx->data); memset(ctx->data, 0, 56); } // Append to the padding the total message's length in bits and transform. ctx->bitlen += ctx->datalen * 8; ctx->data[63] = ctx->bitlen; ctx->data[62] = ctx->bitlen >> 8; ctx->data[61] = ctx->bitlen >> 16; ctx->data[60] = ctx->bitlen >> 24; ctx->data[59] = ctx->bitlen >> 32; ctx->data[58] = ctx->bitlen >> 40; ctx->data[57] = ctx->bitlen >> 48; ctx->data[56] = ctx->bitlen >> 56; sha256_transform(ctx, ctx->data); // Since this implementation uses little endian byte ordering and SHA uses big endian, // reverse all the bytes when copying the final state to the output hash. for (i = 0; i < 4; ++i) { hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; } } estkme-group-lpac-c2fcf5e/euicc/sha256.h000066400000000000000000000023261504765665400201260ustar00rootroot00000000000000/********************************************************************* * Filename: sha256.h * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Defines the API for the corresponding SHA1 implementation. *********************************************************************/ #ifndef SHA256_H #define SHA256_H /*************************** HEADER FILES ***************************/ #include /****************************** MACROS ******************************/ #define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest /**************************** DATA TYPES ****************************/ typedef unsigned char BYTE; // 8-bit byte typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines typedef struct { BYTE data[64]; WORD datalen; unsigned long long bitlen; WORD state[8]; } EUICC_SHA256_CTX; /*********************** FUNCTION DECLARATIONS **********************/ void euicc_sha256_init(EUICC_SHA256_CTX *ctx); void euicc_sha256_update(EUICC_SHA256_CTX *ctx, const BYTE data[], size_t len); void euicc_sha256_final(EUICC_SHA256_CTX *ctx, BYTE hash[]); #endif // SHA256_H estkme-group-lpac-c2fcf5e/euicc/tostr.c000066400000000000000000000106611504765665400202650ustar00rootroot00000000000000#include "tostr.h" const char *euicc_profilestate2str(enum es10c_profile_state value) { switch (value) { case ES10C_PROFILE_STATE_NULL: return NULL; case ES10C_PROFILE_STATE_DISABLED: return "disabled"; case ES10C_PROFILE_STATE_ENABLED: return "enabled"; case ES10C_PROFILE_STATE_UNDEFINED: return "unknown"; } return "(no_str_available)"; } const char *euicc_profileclass2str(enum es10c_profile_class value) { switch (value) { case ES10C_PROFILE_CLASS_NULL: return NULL; case ES10C_PROFILE_CLASS_TEST: return "test"; case ES10C_PROFILE_CLASS_PROVISIONING: return "provisioning"; case ES10C_PROFILE_CLASS_OPERATIONAL: return "operational"; case ES10C_PROFILE_CLASS_UNDEFINED: return "unknown"; } return "(no_str_available)"; } const char *euicc_icontype2str(enum es10c_icon_type value) { switch (value) { case ES10C_ICON_TYPE_NULL: return NULL; case ES10C_ICON_TYPE_JPEG: return "jpeg"; case ES10C_ICON_TYPE_PNG: return "png"; case ES10C_ICON_TYPE_UNDEFINED: return "unknown"; } return "(no_str_available)"; } const char *euicc_profilemanagementoperation2str(enum es10b_profile_management_operation value) { switch (value) { case ES10B_PROFILE_MANAGEMENT_OPERATION_NULL: return NULL; case ES10B_PROFILE_MANAGEMENT_OPERATION_INSTALL: return "install"; case ES10B_PROFILE_MANAGEMENT_OPERATION_ENABLE: return "enable"; case ES10B_PROFILE_MANAGEMENT_OPERATION_DISABLE: return "disable"; case ES10B_PROFILE_MANAGEMENT_OPERATION_DELETE: return "delete"; case ES10B_PROFILE_MANAGEMENT_OPERATION_UNDEFINED: return "unknown"; } return "(no_str_available)"; } const char *euicc_bppcommandid2str(enum es10b_bpp_command_id value) { switch (value) { case ES10B_BPP_COMMAND_ID_INITIALISE_SECURE_CHANNEL: return "initialise_secure_channel"; case ES10B_BPP_COMMAND_ID_CONFIGURE_ISDP: return "configure_isdp"; case ES10B_BPP_COMMAND_ID_STORE_METADATA: return "store_metadata"; case ES10B_BPP_COMMAND_ID_STORE_METADATA2: return "store_metadata2"; case ES10B_BPP_COMMAND_ID_REPLACE_SESSION_KEYS: return "replace_session_keys"; case ES10B_BPP_COMMAND_ID_LOAD_PROFILE_ELEMENTS: return "load_profile_elements"; case ES10B_BPP_COMMAND_ID_UNDEFINED: return "unknown"; } return "(no_str_available)"; } const char *euicc_errorreason2str(enum es10b_error_reason value) { switch (value) { case ES10B_ERROR_REASON_INCORRECT_INPUT_VALUES: return "incorrect_input_values"; case ES10B_ERROR_REASON_INVALID_SIGNATURE: return "invalid_signature"; case ES10B_ERROR_REASON_INVALID_TRANSACTION_ID: return "invalid_transaction_id"; case ES10B_ERROR_REASON_UNSUPPORTED_CRT_VALUES: return "unsupported_crt_values"; case ES10B_ERROR_REASON_UNSUPPORTED_REMOTE_OPERATION_TYPE: return "unsupported_remote_operation_type"; case ES10B_ERROR_REASON_UNSUPPORTED_PROFILE_CLASS: return "unsupported_profile_class"; case ES10B_ERROR_REASON_SCP03T_STRUCTURE_ERROR: return "scp03t_structure_error"; case ES10B_ERROR_REASON_SCP03T_SECURITY_ERROR: return "scp03t_security_error"; case ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_ICCID_ALREADY_EXISTS_ON_EUICC: return "install_failed_due_to_iccid_already_exists_on_euicc"; case ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_INSUFFICIENT_MEMORY_FOR_PROFILE: return "install_failed_due_to_insufficient_memory_for_profile"; case ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_INTERRUPTION: return "install_failed_due_to_interruption"; case ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_PE_PROCESSING_ERROR: return "install_failed_due_to_pe_processing_error"; case ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_DATA_MISMATCH: return "install_failed_due_to_data_mismatch"; case ES10B_ERROR_REASON_TEST_PROFILE_INSTALL_FAILED_DUE_TO_INVALID_NAA_KEY: return "test_profile_install_failed_due_to_invalid_naa_key"; case ES10B_ERROR_REASON_PPR_NOT_ALLOWED: return "ppr_not_allowed"; case ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_UNKNOWN_ERROR: return "install_failed_due_to_unknown_error"; case ES10B_ERROR_REASON_UNDEFINED: return "unknown"; } return "(no_str_available)"; } estkme-group-lpac-c2fcf5e/euicc/tostr.h000066400000000000000000000010141504765665400202620ustar00rootroot00000000000000#pragma once #include "es10b.h" #include "es10c.h" #include #include const char *euicc_profilestate2str(enum es10c_profile_state value); const char *euicc_profileclass2str(enum es10c_profile_class value); const char *euicc_icontype2str(enum es10c_icon_type value); const char *euicc_profilemanagementoperation2str(enum es10b_profile_management_operation value); const char *euicc_bppcommandid2str(enum es10b_bpp_command_id value); const char *euicc_errorreason2str(enum es10b_error_reason value); estkme-group-lpac-c2fcf5e/src/000077500000000000000000000000001504765665400164415ustar00rootroot00000000000000estkme-group-lpac-c2fcf5e/src/CMakeLists.txt000066400000000000000000000022311504765665400211770ustar00rootroot00000000000000if(APPLE) set(RPATH_BINARY_PATH "@loader_path") else() set(RPATH_BINARY_PATH "$ORIGIN") endif() aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} DIR_LPAC_SRCS) aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/applet DIR_LPAC_SRCS) aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/applet/chip DIR_LPAC_SRCS) aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/applet/notification DIR_LPAC_SRCS) aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/applet/profile DIR_LPAC_SRCS) add_executable(lpac ${DIR_LPAC_SRCS}) target_link_libraries(lpac euicc-drivers lpac-utils) target_include_directories(lpac PUBLIC $) find_package(Git) add_custom_target(version ${CMAKE_COMMAND} -D SRC=${CMAKE_CURRENT_SOURCE_DIR}/version.h.in -D DST=${CMAKE_CURRENT_SOURCE_DIR}/version.h -D GIT_EXECUTABLE=${GIT_EXECUTABLE} -P ${LPAC_CMAKE_MODULE_PATH}/git-version.cmake ) add_dependencies(lpac version) set_target_properties(lpac PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/output" BUILD_RPATH "${RPATH_BINARY_PATH}" ) if(UNIX) install(TARGETS lpac RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") endif() estkme-group-lpac-c2fcf5e/src/LICENSE000066400000000000000000001033331504765665400174510ustar00rootroot00000000000000 GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software. A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public. The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version. An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU Affero General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Remote Network Interaction; Use with the GNU General Public License. Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU Affero General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements. You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see . estkme-group-lpac-c2fcf5e/src/applet.c000066400000000000000000000016251504765665400200760ustar00rootroot00000000000000#include "applet.h" #include #include static void applet_usage(const char *selfname, const struct applet_entry **entries) { const struct applet_entry *entry; printf("Usage: %s <", selfname); while ((entry = *entries)) { printf("%s", entry->name); if (*(++entries)) { printf("|"); } } printf(">\n"); } int applet_entry(int argc, char **argv, const struct applet_entry **entries) { const struct applet_entry **entries_cpy; const struct applet_entry *entry; entries_cpy = entries; if (argc < 2) { applet_usage(argv[0], entries); return -1; } while ((entry = *entries_cpy++)) { if (strcmp(argv[1], entry->name) == 0) { return entry->main(argc - 1, argv + 1); } } printf("Unknown command: %s\n", argv[1]); applet_usage(argv[0], entries); return -1; } estkme-group-lpac-c2fcf5e/src/applet.h000066400000000000000000000002641504765665400201010ustar00rootroot00000000000000#pragma once struct applet_entry { const char *name; int (*main)(int argc, char **argv); }; int applet_entry(int argc, char **argv, const struct applet_entry **entries); estkme-group-lpac-c2fcf5e/src/applet/000077500000000000000000000000001504765665400177265ustar00rootroot00000000000000estkme-group-lpac-c2fcf5e/src/applet/chip.c000066400000000000000000000011261504765665400210150ustar00rootroot00000000000000#include "chip.h" #include "chip/defaultsmdp.h" #include "chip/info.h" #include "chip/purge.h" #include "main.h" #include #include #include #include static const struct applet_entry *applets[] = { &applet_chip_info, &applet_chip_defaultsmdp, &applet_chip_purge, NULL, }; static int applet_main(const int argc, char **argv) { const int ret = main_init_euicc(); if (ret != 0) return ret; return applet_entry(argc, argv, applets); } struct applet_entry applet_chip = { .name = "chip", .main = applet_main, }; estkme-group-lpac-c2fcf5e/src/applet/chip.h000066400000000000000000000001131504765665400210150ustar00rootroot00000000000000#pragma once #include extern struct applet_entry applet_chip; estkme-group-lpac-c2fcf5e/src/applet/chip/000077500000000000000000000000001504765665400206515ustar00rootroot00000000000000estkme-group-lpac-c2fcf5e/src/applet/chip/defaultsmdp.c000066400000000000000000000011621504765665400233250ustar00rootroot00000000000000#include "defaultsmdp.h" #include #include #include #include #include #include static int applet_main(int argc, char **argv) { const char *smdp; if (argc < 2) { printf("Usage: %s \n", argv[0]); return -1; } smdp = argv[1]; if (es10a_set_default_dp_address(&euicc_ctx, smdp)) { jprint_error("es10a_set_default_dp_address", NULL); return -1; } jprint_success(NULL); return 0; } struct applet_entry applet_chip_defaultsmdp = { .name = "defaultsmdp", .main = applet_main, }; estkme-group-lpac-c2fcf5e/src/applet/chip/defaultsmdp.h000066400000000000000000000001271504765665400233320ustar00rootroot00000000000000#pragma once #include extern struct applet_entry applet_chip_defaultsmdp; estkme-group-lpac-c2fcf5e/src/applet/chip/info.c000066400000000000000000000166741504765665400217660ustar00rootroot00000000000000#include "info.h" #include "main.h" #include #include #include #include #include #include #include #include #include static int applet_main(int argc, char **argv) { _cleanup_free_ char *eid = NULL; _cleanup_(es10a_euicc_configured_addresses_free) struct es10a_euicc_configured_addresses addresses; _cleanup_es10b_rat_list_ struct es10b_rat *ratList; _cleanup_(es10c_ex_euiccinfo2_free) struct es10c_ex_euiccinfo2 euiccinfo2; cJSON *jaddresses = NULL, *jratList = NULL, *jeuiccinfo2 = NULL, *jdata = NULL; if (es10c_get_eid(&euicc_ctx, &eid)) { jprint_error("es10c_get_eid", NULL); return -1; } if (es10a_get_euicc_configured_addresses(&euicc_ctx, &addresses) == 0) { jaddresses = cJSON_CreateObject(); } if (es10b_get_rat(&euicc_ctx, &ratList) == 0) { jratList = cJSON_CreateArray(); } if (es10c_ex_get_euiccinfo2(&euicc_ctx, &euiccinfo2) == 0) { jeuiccinfo2 = cJSON_CreateObject(); } jdata = cJSON_CreateObject(); cJSON_AddStringOrNullToObject(jdata, "eidValue", eid); if (jaddresses) { cJSON_AddStringOrNullToObject(jaddresses, "defaultDpAddress", addresses.defaultDpAddress); cJSON_AddStringOrNullToObject(jaddresses, "rootDsAddress", addresses.rootDsAddress); } cJSON_AddItemToObject(jdata, "EuiccConfiguredAddresses", jaddresses); if (jeuiccinfo2) { cJSON_AddStringOrNullToObject(jeuiccinfo2, "profileVersion", euiccinfo2.profileVersion); cJSON_AddStringOrNullToObject(jeuiccinfo2, "svn", euiccinfo2.svn); cJSON_AddStringOrNullToObject(jeuiccinfo2, "euiccFirmwareVer", euiccinfo2.euiccFirmwareVer); { cJSON *jextCardResource = cJSON_CreateObject(); cJSON_AddNumberToObject(jextCardResource, "installedApplication", euiccinfo2.extCardResource.installedApplication); cJSON_AddNumberToObject(jextCardResource, "freeNonVolatileMemory", euiccinfo2.extCardResource.freeNonVolatileMemory); cJSON_AddNumberToObject(jextCardResource, "freeVolatileMemory", euiccinfo2.extCardResource.freeVolatileMemory); cJSON_AddItemToObject(jeuiccinfo2, "extCardResource", jextCardResource); } if (euiccinfo2.uiccCapability) { cJSON *juiccCapability = cJSON_CreateArray(); for (int i = 0; euiccinfo2.uiccCapability[i] != NULL; i++) { cJSON_AddItemToArray(juiccCapability, cJSON_CreateString(euiccinfo2.uiccCapability[i])); } cJSON_AddItemToObject(jeuiccinfo2, "uiccCapability", juiccCapability); } cJSON_AddStringOrNullToObject(jeuiccinfo2, "ts102241Version", euiccinfo2.ts102241Version); cJSON_AddStringOrNullToObject(jeuiccinfo2, "globalplatformVersion", euiccinfo2.globalplatformVersion); if (euiccinfo2.rspCapability) { cJSON *jrspCapability = cJSON_CreateArray(); for (int i = 0; euiccinfo2.rspCapability[i] != NULL; i++) { cJSON_AddItemToArray(jrspCapability, cJSON_CreateString(euiccinfo2.rspCapability[i])); } cJSON_AddItemToObject(jeuiccinfo2, "rspCapability", jrspCapability); } if (euiccinfo2.euiccCiPKIdListForVerification) { cJSON *verification_keys = cJSON_CreateArray(); for (int i = 0; euiccinfo2.euiccCiPKIdListForVerification[i] != NULL; i++) { cJSON_AddItemToArray(verification_keys, cJSON_CreateString(euiccinfo2.euiccCiPKIdListForVerification[i])); } cJSON_AddItemToObject(jeuiccinfo2, "euiccCiPKIdListForVerification", verification_keys); } if (euiccinfo2.euiccCiPKIdListForSigning) { cJSON *signing_keys = cJSON_CreateArray(); for (int i = 0; euiccinfo2.euiccCiPKIdListForSigning[i] != NULL; i++) { cJSON_AddItemToArray(signing_keys, cJSON_CreateString(euiccinfo2.euiccCiPKIdListForSigning[i])); } cJSON_AddItemToObject(jeuiccinfo2, "euiccCiPKIdListForSigning", signing_keys); } cJSON_AddStringOrNullToObject(jeuiccinfo2, "euiccCategory", euiccinfo2.euiccCategory); if (euiccinfo2.forbiddenProfilePolicyRules) { cJSON *jforbiddenProfilePolicyRules = cJSON_CreateArray(); for (int i = 0; euiccinfo2.forbiddenProfilePolicyRules[i] != NULL; i++) { cJSON_AddItemToArray(jforbiddenProfilePolicyRules, cJSON_CreateString(euiccinfo2.forbiddenProfilePolicyRules[i])); } cJSON_AddItemToObject(jeuiccinfo2, "forbiddenProfilePolicyRules", jforbiddenProfilePolicyRules); } cJSON_AddStringOrNullToObject(jeuiccinfo2, "ppVersion", euiccinfo2.ppVersion); cJSON_AddStringOrNullToObject(jeuiccinfo2, "sasAcreditationNumber", euiccinfo2.sasAcreditationNumber); { cJSON *jcertificationDataObject = cJSON_CreateObject(); cJSON_AddStringOrNullToObject(jcertificationDataObject, "platformLabel", euiccinfo2.certificationDataObject.platformLabel); cJSON_AddStringOrNullToObject(jcertificationDataObject, "discoveryBaseURL", euiccinfo2.certificationDataObject.discoveryBaseURL); cJSON_AddItemToObject(jeuiccinfo2, "certificationDataObject", jcertificationDataObject); } } cJSON_AddItemToObject(jdata, "EUICCInfo2", jeuiccinfo2); if (jratList) { while (ratList) { struct cJSON *jrat = cJSON_CreateObject(); if (ratList->pprIds) { cJSON *jPPR = cJSON_CreateArray(); for (int i = 0; ratList->pprIds[i] != NULL; i++) { cJSON_AddItemToArray(jPPR, cJSON_CreateString(ratList->pprIds[i])); } cJSON_AddItemToObject(jrat, "pprIds", jPPR); } if (ratList->allowedOperators) { cJSON *jAllowedOperators = cJSON_CreateArray(); const struct es10b_operation_id *rptr = ratList->allowedOperators; while (rptr) { cJSON *joperator = cJSON_CreateObject(); cJSON_AddStringOrNullToObject(joperator, "plmn", rptr->plmn); cJSON_AddStringOrNullToObject(joperator, "gid1", rptr->gid1); cJSON_AddStringOrNullToObject(joperator, "gid2", rptr->gid2); cJSON_AddItemToArray(jAllowedOperators, joperator); rptr = rptr->next; } cJSON_AddItemToObject(jrat, "allowedOperators", jAllowedOperators); } if (ratList->pprFlags) { cJSON *jFlags = cJSON_CreateArray(); for (int i = 0; ratList->pprFlags[i] != NULL; i++) { cJSON_AddItemToArray(jFlags, cJSON_CreateString(ratList->pprFlags[i])); } cJSON_AddItemToObject(jrat, "pprFlags", jFlags); } cJSON_AddItemToArray(jratList, jrat); ratList = ratList->next; } cJSON_AddItemToObject(jdata, "rulesAuthorisationTable", jratList); } jprint_success(jdata); return 0; } struct applet_entry applet_chip_info = { .name = "info", .main = applet_main, }; estkme-group-lpac-c2fcf5e/src/applet/chip/info.h000066400000000000000000000001201504765665400217460ustar00rootroot00000000000000#pragma once #include extern struct applet_entry applet_chip_info; estkme-group-lpac-c2fcf5e/src/applet/chip/purge.c000066400000000000000000000016701504765665400221430ustar00rootroot00000000000000#include "purge.h" #include "main.h" #include #include #include #include #include static int applet_main(int argc, char **argv) { int ret; if (argc < 2) { printf("Usage: %s [yes|other]\n", argv[0]); printf("\t\tConfirm purge eUICC, all data will lost!\n"); return -1; } if (strcmp(argv[1], "yes") != 0) { printf("Purge canceled\n"); return -1; } if ((ret = es10c_euicc_memory_reset(&euicc_ctx))) { const char *reason; switch (ret) { case 1: reason = "nothing to delete"; break; default: reason = "unknown"; break; } jprint_error("es10c_euicc_memory_reset", reason); return -1; } jprint_success(NULL); return 0; } struct applet_entry applet_chip_purge = { .name = "purge", .main = applet_main, }; estkme-group-lpac-c2fcf5e/src/applet/chip/purge.h000066400000000000000000000001211504765665400221360ustar00rootroot00000000000000#pragma once #include extern struct applet_entry applet_chip_purge; estkme-group-lpac-c2fcf5e/src/applet/notification.c000066400000000000000000000014111504765665400225550ustar00rootroot00000000000000#include "notification.h" #include "main.h" #include "notification/dump.h" #include "notification/list.h" #include "notification/process.h" #include "notification/remove.h" #include "notification/replay.h" #include #include #include #include static const struct applet_entry *applets[] = { &applet_notification_list, &applet_notification_process, &applet_notification_remove, &applet_notification_dump, &applet_notification_replay, NULL, }; static int applet_main(const int argc, char **argv) { const int ret = main_init_euicc(); if (ret != 0) return ret; return applet_entry(argc, argv, applets); } struct applet_entry applet_notification = { .name = "notification", .main = applet_main, }; estkme-group-lpac-c2fcf5e/src/applet/notification.h000066400000000000000000000001231504765665400225610ustar00rootroot00000000000000#pragma once #include extern struct applet_entry applet_notification; estkme-group-lpac-c2fcf5e/src/applet/notification/000077500000000000000000000000001504765665400224145ustar00rootroot00000000000000estkme-group-lpac-c2fcf5e/src/applet/notification/dump.c000066400000000000000000000054531504765665400235340ustar00rootroot00000000000000#include "notification_common.h" #include "process.h" #include #include #include #include #include #include #include #include #include static bool retrieve_notification(const char *eid, const uint32_t seqNumber) { _cleanup_(es10b_pending_notification_free) struct es10b_pending_notification notification; if (es10b_retrieve_notifications_list(&euicc_ctx, ¬ification, seqNumber)) { jprint_error("es10b_retrieve_notifications_list", NULL); return false; } _cleanup_cjson_ cJSON *jroot = build_notification(eid, seqNumber, ¬ification); if (jroot == NULL) return false; _cleanup_free_ char *jstr = cJSON_PrintUnformatted(jroot); printf("%s\n", jstr); fflush(stdout); return true; } static int applet_main(const int argc, char **argv) { static const char *opt_string = "ah?"; int fret = 0; int all = 0; int opt = 0; char *eid = NULL; while ((opt = getopt(argc, argv, opt_string)) != -1) { switch (opt) { case 'a': all = 1; break; case 'h': case '?': printf("Usage: %s [OPTIONS] [seqNumber_0] [seqNumber_1]...\n", argv[0]); printf("\t -a All notifications\n"); return -1; default: break; } } if (es10c_get_eid(&euicc_ctx, &eid)) { jprint_error("es10c_get_eid", NULL); return -1; } if (all) { _cleanup_es10b_notification_metadata_list_ struct es10b_notification_metadata_list *notifications, *rptr; if (es10b_list_notification(&euicc_ctx, ¬ifications)) { jprint_error("es10b_list_notification", NULL); return -1; } rptr = notifications; while (rptr) { if (!retrieve_notification(eid, rptr->seqNumber)) { fret = -1; break; } rptr = rptr->next; } } else { for (int i = optind; i < argc; i++) { errno = 0; char *str_end; const unsigned long seqNumber = strtoul(argv[i], &str_end, 10); // Although POSIX said user should check errno instead of return value, // but errno may not be set when no conversion is performed according to C99. // Check nptr is same as str_end to ensure there is no conversion. if ((seqNumber == 0 && strcmp(argv[i], str_end)) || errno != 0) { continue; } if (!retrieve_notification(eid, seqNumber)) { fret = -1; break; } } } return fret; } struct applet_entry applet_notification_dump = { .name = "dump", .main = applet_main, }; estkme-group-lpac-c2fcf5e/src/applet/notification/dump.h000066400000000000000000000001301504765665400235240ustar00rootroot00000000000000#pragma once #include extern struct applet_entry applet_notification_dump; estkme-group-lpac-c2fcf5e/src/applet/notification/list.c000066400000000000000000000026121504765665400235340ustar00rootroot00000000000000#include "list.h" #include "main.h" #include "notification_common.h" #include #include #include #include #include static int applet_main(int argc, char **argv) { _cleanup_es10b_notification_metadata_list_ struct es10b_notification_metadata_list *notifications, *rptr; cJSON *jdata = NULL; if (es10b_list_notification(&euicc_ctx, ¬ifications)) { jprint_error("es10b_list_notification", NULL); return -1; } jdata = cJSON_CreateArray(); rptr = notifications; while (rptr) { cJSON *jnotification = NULL; jnotification = cJSON_CreateObject(); cJSON_AddNumberToObject(jnotification, "seqNumber", rptr->seqNumber); cJSON_AddStringOrNullToObject(jnotification, "profileManagementOperation", euicc_profilemanagementoperation2str(rptr->profileManagementOperation)); cJSON_AddStringOrNullToObject(jnotification, "notificationAddress", notification_strstrip(rptr->notificationAddress)); cJSON_AddStringOrNullToObject(jnotification, "iccid", rptr->iccid); cJSON_AddItemToArray(jdata, jnotification); rptr = rptr->next; } jprint_success(jdata); return 0; } struct applet_entry applet_notification_list = { .name = "list", .main = applet_main, }; estkme-group-lpac-c2fcf5e/src/applet/notification/list.h000066400000000000000000000001301504765665400235320ustar00rootroot00000000000000#pragma once #include extern struct applet_entry applet_notification_list; estkme-group-lpac-c2fcf5e/src/applet/notification/notification_common.c000066400000000000000000000046251504765665400266250ustar00rootroot00000000000000#include "notification_common.h" #include #include char *notification_strstrip(char *input) { if (input == NULL) return NULL; // Remove leading whitespace while (isspace((unsigned char)*input)) { input++; } // Remove trailing whitespace if (*input == '\0') return input; // Check if the string is not empty after leading whitespace removal char *end = input + strlen(input) - 1; while (end >= input && isspace((unsigned char)*end)) { *end = '\0'; end--; } return input; } cJSON *build_notification(const char *eid, const uint32_t seqNumber, const struct es10b_pending_notification *notification) { cJSON *jroot = cJSON_CreateObject(); if (jroot == NULL) return jroot; cJSON_AddStringToObject(jroot, "type", "notification"); cJSON_AddStringToObject(jroot, "eid", eid); cJSON_AddNumberToObject(jroot, "seqNumber", seqNumber); cJSON_AddStringToObject(jroot, "notificationAddress", notification_strstrip(notification->notificationAddress)); cJSON_AddStringToObject(jroot, "pendingNotification", notification->b64_PendingNotification); return jroot; } bool parse_notification(const cJSON *jroot, const char *eid, uint32_t *seqNumber, struct es10b_pending_notification *notification) { const char *value = NULL; const cJSON *jvalue = NULL; jvalue = cJSON_GetObjectItem(jroot, "type"); if (!cJSON_IsString(jvalue)) return false; value = cJSON_GetStringValue(jvalue); if (strcmp(value, "notification") != 0) return false; jvalue = cJSON_GetObjectItem(jroot, "eid"); if (!cJSON_IsString(jvalue)) return false; value = cJSON_GetStringValue(jvalue); if (strcmp(value, eid) != 0) return false; jvalue = cJSON_GetObjectItem(jroot, "seqNumber"); if (!cJSON_IsNumber(jvalue)) return false; *seqNumber = (uint32_t)cJSON_GetNumberValue(jvalue); jvalue = cJSON_GetObjectItem(jroot, "notificationAddress"); if (!cJSON_IsString(jvalue)) return false; notification->notificationAddress = notification_strstrip(cJSON_GetStringValue(jvalue)); jvalue = cJSON_GetObjectItem(jroot, "pendingNotification"); if (!cJSON_IsString(jvalue)) return false; notification->b64_PendingNotification = cJSON_GetStringValue(jvalue); return true; } estkme-group-lpac-c2fcf5e/src/applet/notification/notification_common.h000066400000000000000000000006261504765665400266270ustar00rootroot00000000000000#pragma once #include #include #include char *notification_strstrip(char *input); cJSON *build_notification(const char *eid, uint32_t seqNumber, const struct es10b_pending_notification *notification); bool parse_notification(const cJSON *jroot, const char *eid, uint32_t *seqNumber, struct es10b_pending_notification *notification); estkme-group-lpac-c2fcf5e/src/applet/notification/process.c000066400000000000000000000073011504765665400242370ustar00rootroot00000000000000#include "process.h" #include "notification_common.h" #include #include #include #include #include #include #include #include #include static int _process_single(uint32_t seqNumber, uint8_t autoremove) { int ret; char str_seqNumber[11]; _cleanup_(es10b_pending_notification_free) struct es10b_pending_notification notification; snprintf(str_seqNumber, sizeof(str_seqNumber), "%u", seqNumber); jprint_progress("es10b_retrieve_notifications_list", str_seqNumber); if (es10b_retrieve_notifications_list(&euicc_ctx, ¬ification, seqNumber)) { jprint_error("es10b_retrieve_notifications_list", NULL); return -1; } euicc_ctx.http.server_address = notification_strstrip(notification.notificationAddress); jprint_progress("es9p_handle_notification", str_seqNumber); if (es9p_handle_notification(&euicc_ctx, notification.b64_PendingNotification)) { jprint_error("es9p_handle_notification", NULL); return -1; } if (!autoremove) { return 0; } jprint_progress("es10b_remove_notification_from_list", str_seqNumber); if ((ret = es10b_remove_notification_from_list(&euicc_ctx, seqNumber))) { const char *reason; switch (ret) { case 1: reason = "seqNumber not found"; break; default: reason = "unknown"; break; } jprint_error("es10b_remove_notification_from_list", reason); return -1; } return 0; } static int applet_main(int argc, char **argv) { static const char *opt_string = "arh?"; int fret = 0; int all = 0; int autoremove = 0; int opt = 0; while ((opt = getopt(argc, argv, opt_string)) != -1) { switch (opt) { case 'a': all = 1; break; case 'r': autoremove = 1; break; case 'h': case '?': printf("Usage: %s [OPTIONS] [seqNumber_0] [seqNumber_1]...\n", argv[0]); printf("\t -a All notifications\n"); printf("\t -r Automatically remove processed notifications\n"); return -1; default: break; } } if (all) { _cleanup_es10b_notification_metadata_list_ struct es10b_notification_metadata_list *notifications, *rptr; jprint_progress("es10b_list_notification", NULL); if (es10b_list_notification(&euicc_ctx, ¬ifications)) { jprint_error("es10b_list_notification", NULL); return -1; } rptr = notifications; while (rptr) { if (_process_single(rptr->seqNumber, autoremove)) { fret = -1; break; } rptr = rptr->next; } } else { for (int i = optind; i < argc; i++) { unsigned long seqNumber; errno = 0; char *str_end; seqNumber = strtoul(argv[i], &str_end, 10); // Although POSIX said user should check errno instead of return value, // but errno may not be set when no conversion is performed according to C99. // Check nptr is same as str_end to ensure there is no conversion. if ((seqNumber == 0 && strcmp(argv[i], str_end)) || errno != 0) { continue; } if (_process_single(seqNumber, autoremove)) { fret = -1; break; } } } if (fret == 0) { jprint_success(NULL); } return fret; } struct applet_entry applet_notification_process = { .name = "process", .main = applet_main, }; estkme-group-lpac-c2fcf5e/src/applet/notification/process.h000066400000000000000000000001331504765665400242400ustar00rootroot00000000000000#pragma once #include extern struct applet_entry applet_notification_process; estkme-group-lpac-c2fcf5e/src/applet/notification/remove.c000066400000000000000000000053451504765665400240640ustar00rootroot00000000000000#include "remove.h" #include #include #include #include #include #include #include #include static int _delete_single(uint32_t seqNumber) { char str_seqNumber[11]; int ret; snprintf(str_seqNumber, sizeof(str_seqNumber), "%u", seqNumber); jprint_progress("es10b_remove_notification_from_list", str_seqNumber); if ((ret = es10b_remove_notification_from_list(&euicc_ctx, seqNumber))) { const char *reason; switch (ret) { case 1: reason = "seqNumber not found"; break; default: reason = "unknown"; break; } jprint_error("es10b_remove_notification_from_list", reason); return -1; } return 0; } static int applet_main(int argc, char **argv) { static const char *opt_string = "ah?"; int fret = 0; int all = 0; int opt = 0; while ((opt = getopt(argc, argv, opt_string)) != -1) { switch (opt) { case 'a': all = 1; break; case 'h': case '?': printf("Usage: %s [OPTIONS] [seqNumber_0] [seqNumber_1]...\n", argv[0]); printf("\t -a All notifications\n"); return -1; default: break; } } if (all) { _cleanup_es10b_notification_metadata_list_ struct es10b_notification_metadata_list *notifications, *rptr; jprint_progress("es10b_list_notification", NULL); if (es10b_list_notification(&euicc_ctx, ¬ifications)) { jprint_error("es10b_list_notification", NULL); return -1; } rptr = notifications; while (rptr) { if (_delete_single(rptr->seqNumber)) { fret = -1; break; } rptr = rptr->next; } } else { for (int i = optind; i < argc; i++) { unsigned long seqNumber; errno = 0; char *str_end; seqNumber = strtoul(argv[i], &str_end, 10); // Although POSIX said user should check errno instead of return value, // but errno may not be set when no conversion is performed according to C99. // Check nptr is same as str_end to ensure there is no conversion. if ((seqNumber == 0 && strcmp(argv[i], str_end)) || errno != 0) { continue; } if (_delete_single(seqNumber)) { fret = -1; break; } } } if (fret == 0) { jprint_success(NULL); } return fret; } struct applet_entry applet_notification_remove = { .name = "remove", .main = applet_main, }; estkme-group-lpac-c2fcf5e/src/applet/notification/remove.h000066400000000000000000000001321504765665400240560ustar00rootroot00000000000000#pragma once #include extern struct applet_entry applet_notification_remove; estkme-group-lpac-c2fcf5e/src/applet/notification/replay.c000066400000000000000000000060671504765665400240650ustar00rootroot00000000000000#include "notification_common.h" #include "process.h" #include #include #include #include #include #include #include #include #include #ifdef _WIN32 ssize_t getline(char **lineptr, size_t *n, FILE *stream) { if (!lineptr || !n || !stream) { return -1; // Invalid arguments } // Initial buffer size size_t initial_size = 128; if (*lineptr == NULL || *n == 0) { *n = initial_size; *lineptr = (char *)malloc(*n); if (*lineptr == NULL) { return -1; // Memory allocation failed } } size_t len = 0; int c; while ((c = fgetc(stream)) != EOF) { if (len + 1 >= *n) { // +1 for null terminator size_t new_size = *n * 2; char *new_ptr = (char *)realloc(*lineptr, new_size); if (new_ptr == NULL) { return -1; // Memory reallocation failed } *lineptr = new_ptr; *n = new_size; } (*lineptr)[len++] = (char)c; if (c == '\n') { break; // End of line } } if (c == EOF) { return -1; // No newline found and EOF reached } (*lineptr)[len] = '\0'; // Null-terminate the string return len; } #endif static int handle_notification(const uint32_t seqNumber, const struct es10b_pending_notification notification) { char str_seqNumber[11]; snprintf(str_seqNumber, sizeof(str_seqNumber), "%u", seqNumber); euicc_ctx.http.server_address = notification.notificationAddress; jprint_progress("es9p_handle_notification", str_seqNumber); if (es9p_handle_notification(&euicc_ctx, notification.b64_PendingNotification)) { jprint_error("es9p_handle_notification", NULL); return -1; } return 0; } static int applet_main(const int argc, char **argv) { if (isatty(fileno(stdin))) { jprint_error("This applet must be run with input redirection from a file or pipe.", NULL); return -1; } char *input = NULL; char *eid = NULL; uint32_t seqNumber = 0; if (es10c_get_eid(&euicc_ctx, &eid) != 0) { jprint_error("es10c_get_eid", NULL); return -1; } size_t n; _cleanup_(es10b_pending_notification_free) struct es10b_pending_notification notification; while (getline(&input, &n, stdin) != -1) { _cleanup_cjson_ cJSON *jroot = cJSON_ParseWithLength(input, n); if (jroot == NULL) { jprint_error("cJSON_ParseWithLength", NULL); return -1; } if (parse_notification(jroot, eid, &seqNumber, ¬ification) != 0) { jprint_error("parse_notification", NULL); return -1; } if (handle_notification(seqNumber, notification) != 0) { jprint_error("handle_notification", NULL); return -1; } } jprint_success(NULL); return 0; } struct applet_entry applet_notification_replay = { .name = "replay", .main = applet_main, }; estkme-group-lpac-c2fcf5e/src/applet/notification/replay.h000066400000000000000000000001321504765665400240550ustar00rootroot00000000000000#pragma once #include extern struct applet_entry applet_notification_replay; estkme-group-lpac-c2fcf5e/src/applet/profile.c000066400000000000000000000015071504765665400215350ustar00rootroot00000000000000#include "profile.h" #include "main.h" #include "profile/delete.h" #include "profile/disable.h" #include "profile/discovery.h" #include "profile/download.h" #include "profile/enable.h" #include "profile/list.h" #include "profile/nickname.h" #include #include #include #include static const struct applet_entry *applets[] = { &applet_profile_list, &applet_profile_enable, &applet_profile_disable, &applet_profile_nickname, &applet_profile_delete, &applet_profile_download, &applet_profile_discovery, NULL, }; static int applet_main(const int argc, char **argv) { const int ret = main_init_euicc(); if (ret != 0) return ret; return applet_entry(argc, argv, applets); } struct applet_entry applet_profile = { .name = "profile", .main = applet_main, }; estkme-group-lpac-c2fcf5e/src/applet/profile.h000066400000000000000000000001161504765665400215350ustar00rootroot00000000000000#pragma once #include extern struct applet_entry applet_profile; estkme-group-lpac-c2fcf5e/src/applet/profile/000077500000000000000000000000001504765665400213665ustar00rootroot00000000000000estkme-group-lpac-c2fcf5e/src/applet/profile/delete.c000066400000000000000000000021441504765665400227750ustar00rootroot00000000000000#include "delete.h" #include "main.h" #include #include #include #include #include static int applet_main(int argc, char **argv) { int ret; const char *param; if (argc < 2) { printf("Usage: %s [iccid/aid]\n", argv[0]); return -1; } param = argv[1]; ret = es10c_delete_profile(&euicc_ctx, param); if (ret) { const char *reason; switch (ret) { case 1: reason = "iccid or aid not found"; break; case 2: reason = "profile not in disabled state"; break; case 3: reason = "disallowed by policy"; break; case -1: reason = "internal error, maybe illegal iccid/aid coding"; break; default: reason = "unknown"; break; } jprint_error("es10c_delete_profile", reason); return -1; } jprint_success(NULL); return 0; } struct applet_entry applet_profile_delete = { .name = "delete", .main = applet_main, }; estkme-group-lpac-c2fcf5e/src/applet/profile/delete.h000066400000000000000000000001251504765665400227770ustar00rootroot00000000000000#pragma once #include extern struct applet_entry applet_profile_delete; estkme-group-lpac-c2fcf5e/src/applet/profile/disable.c000066400000000000000000000024341504765665400231400ustar00rootroot00000000000000#include "disable.h" #include "main.h" #include #include #include #include #include static int applet_main(int argc, char **argv) { int ret; const char *param; int refreshflag; if (argc < 2) { printf("Usage: %s [iccid/aid] [refreshflag]\n", argv[0]); printf("\t[refreshflag]: optional\n"); return -1; } param = argv[1]; refreshflag = 0; if (argc > 2) { refreshflag = atoi(argv[2]); } ret = es10c_disable_profile(&euicc_ctx, param, refreshflag); if (ret) { const char *reason; switch (ret) { case 1: reason = "iccid or aid not found"; break; case 2: reason = "profile not in enabled state"; break; case 3: reason = "disallowed by policy"; break; case -1: reason = "internal error, maybe illegal iccid/aid coding"; break; default: reason = "unknown"; break; } jprint_error("es10c_disable_profile", reason); return -1; } jprint_success(NULL); return 0; } struct applet_entry applet_profile_disable = { .name = "disable", .main = applet_main, }; estkme-group-lpac-c2fcf5e/src/applet/profile/disable.h000066400000000000000000000001261504765665400231410ustar00rootroot00000000000000#pragma once #include extern struct applet_entry applet_profile_disable; estkme-group-lpac-c2fcf5e/src/applet/profile/discovery.c000066400000000000000000000050241504765665400235420ustar00rootroot00000000000000#include "discovery.h" #include "main.h" #include #include #include #include #include #include #include #include static const char *opt_string = "s:i:h?"; static int applet_main(int argc, char **argv) { int fret; int opt; char *smds = NULL; char *imei = NULL; _cleanup_es11_smdp_list_ char **smdp_list = NULL; cJSON *jdata = NULL; while ((opt = getopt(argc, argv, opt_string)) != -1) { switch (opt) { case 's': smds = strdup(optarg); break; case 'i': imei = strdup(optarg); break; case 'h': case '?': printf("Usage: %s [OPTIONS]\n", argv[0]); printf("\t -s SM-DS Domain\n"); printf("\t -i IMEI\n"); printf("\t -h This help info\n"); return -1; break; } opt = getopt(argc, argv, opt_string); } if (smds == NULL) { // smds = "prod.smds.rsp.goog"; // smds = "lpa.live.esimdiscovery.com"; smds = "lpa.ds.gsma.com"; } euicc_ctx.http.server_address = smds; jprint_progress("es10b_get_euicc_challenge_and_info", smds); if (es10b_get_euicc_challenge_and_info(&euicc_ctx)) { jprint_error("es10b_get_euicc_challenge_and_info", NULL); goto err; } jprint_progress("es9p_initiate_authentication", smds); if (es9p_initiate_authentication(&euicc_ctx)) { jprint_error("es9p_initiate_authentication", euicc_ctx.http.status.message); goto err; } jprint_progress("es10b_authenticate_server", smds); if (es10b_authenticate_server(&euicc_ctx, NULL, imei)) { jprint_error("es10b_authenticate_server", NULL); goto err; } jprint_progress("es11_authenticate_client", smds); if (es11_authenticate_client(&euicc_ctx, &smdp_list)) { jprint_error("es11_authenticate_client", NULL); goto err; } jdata = cJSON_CreateArray(); if (jdata == NULL) { goto err; } for (int i = 0; smdp_list[i] != NULL; i++) { cJSON *jsmdp = cJSON_CreateString(smdp_list[i]); if (jsmdp == NULL) { goto err; } cJSON_AddItemToArray(jdata, jsmdp); } jprint_success(jdata); fret = 0; goto exit; err: fret = -1; exit: euicc_http_cleanup(&euicc_ctx); return fret; } struct applet_entry applet_profile_discovery = { .name = "discovery", .main = applet_main, }; estkme-group-lpac-c2fcf5e/src/applet/profile/discovery.h000066400000000000000000000001301504765665400235400ustar00rootroot00000000000000#pragma once #include extern struct applet_entry applet_profile_discovery; estkme-group-lpac-c2fcf5e/src/applet/profile/download.c000066400000000000000000000233751504765665400233530ustar00rootroot00000000000000#include "download.h" #include "main.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char *opt_string = "s:m:i:c:a:ph?"; static volatile int cancelled = 0; #define CANCELPOINT() \ if (cancelled) { \ goto err; \ } #ifdef _WIN32 // https://stackoverflow.com/a/58244503 char *strsep(char **stringp, const char *__delim) { char *rv = *stringp; if (!rv) return rv; *stringp += strcspn(*stringp, __delim); if (**stringp) *(*stringp)++ = '\0'; else *stringp = 0; return rv; } #endif static bool is_strict_matching_id(const char *token) { const size_t n = strlen(token); for (int i = 0; i < n; i++) { if (isalnum(token[i]) || token[i] == '-') continue; return false; } return true; } static void sigint_handler(int x) { cancelled = 1; } static cJSON *build_download_result_json(const struct es10b_load_bound_profile_package_result *result) { cJSON *jdata = cJSON_CreateObject(); if (jdata == NULL) { // Memory allocation failed, return NULL to indicate error return NULL; } cJSON_AddNumberToObject(jdata, "seqNumber", (double)result->seqNumber); cJSON_AddStringToObject(jdata, "bppCommandId", euicc_bppcommandid2str(result->bppCommandId)); cJSON_AddStringToObject(jdata, "errorReason", euicc_errorreason2str(result->errorReason)); return jdata; } static int applet_main(int argc, char **argv) { int fret; const char *error_function_name = NULL; _cleanup_free_ char *error_detail = NULL; int opt; char *smdp = NULL; char *matchingId = NULL; char *imei = NULL; char *confirmation_code = NULL; char *activation_code = NULL; int interactive_preview = 0; _cleanup_(es10a_euicc_configured_addresses_free) struct es10a_euicc_configured_addresses configured_addresses = {0}; struct es10b_load_bound_profile_package_result download_result = {0}; cJSON *jmetadata = NULL; _cleanup_(es8p_metadata_free) struct es8p_metadata *profile_metadata = NULL; while ((opt = getopt(argc, argv, opt_string)) != -1) { switch (opt) { case 's': smdp = strdup(optarg); break; case 'm': matchingId = strdup(optarg); break; case 'i': imei = strdup(optarg); break; case 'c': confirmation_code = strdup(optarg); break; case 'a': activation_code = strdup(optarg); if (strncmp(activation_code, "LPA:", 4) == 0) { activation_code += 4; // ignore uri scheme } break; case 'p': interactive_preview = 1; break; case 'h': case '?': printf("Usage: %s [OPTIONS]\n", argv[0]); printf("\t -s SM-DP+ Domain\n"); printf("\t -m Matching ID\n"); printf("\t -i IMEI\n"); printf("\t -c Confirmation Code (Password)\n"); printf("\t -a Activation Code (e.g: 'LPA:***')\n"); printf("\t -p Interactive preview profile\n"); printf("\t -h This help info\n"); return -1; default: break; } } if (activation_code != NULL) { // SGP.22 v2.2.2; Page 111 // Section: 4.1 (Activation Code) const char *token = NULL; int index = 0; while ((token = strsep(&activation_code, "$")) != NULL) { switch (index) { case 0: // Activation Code Format if (strncmp(token, "1", strlen(token)) != 0) { error_function_name = "activation_code"; error_detail = strdup("invalid"); goto err; } break; case 1: // SM-DP+ Address smdp = strdup(token); break; case 2: // AC_Token or Matching ID matchingId = strdup(token); if (!is_strict_matching_id(matchingId)) { error_function_name = "matching_id"; error_detail = strdup("invalid format, contains character not alphanumeric or dash"); goto err; } break; case 3: // SM-DP+ OID // ignored; this function is not implemented break; case 4: // Confirmation Code Required Flag if (strncmp(token, "1", strlen(token)) == 0 && confirmation_code == NULL) { error_function_name = "confirmation_code"; error_detail = strdup("required"); goto err; } break; default: break; } index++; } } if (smdp == NULL) { jprint_progress("es10a_get_euicc_configured_addresses", NULL); if (es10a_get_euicc_configured_addresses(&euicc_ctx, &configured_addresses)) { error_function_name = "es10a_get_euicc_configured_addresses"; error_detail = NULL; goto err; } else { smdp = configured_addresses.defaultDpAddress; } } if (!smdp || (strlen(smdp) == 0)) { error_function_name = "smdp"; error_detail = strdup("empty"); goto err; } signal(SIGINT, sigint_handler); euicc_ctx.http.server_address = smdp; CANCELPOINT(); jprint_progress("es10b_get_euicc_challenge_and_info", smdp); if (es10b_get_euicc_challenge_and_info(&euicc_ctx)) { error_function_name = "es10b_get_euicc_challenge_and_info"; error_detail = NULL; goto err; } CANCELPOINT(); jprint_progress("es9p_initiate_authentication", smdp); if (es9p_initiate_authentication(&euicc_ctx)) { error_function_name = "es9p_initiate_authentication"; error_detail = strdup(euicc_ctx.http.status.message); goto err; } CANCELPOINT(); jprint_progress("es10b_authenticate_server", smdp); if (es10b_authenticate_server(&euicc_ctx, matchingId, imei)) { error_function_name = "es10b_authenticate_server"; error_detail = NULL; goto err; } CANCELPOINT(); jprint_progress("es9p_authenticate_client", smdp); if (es9p_authenticate_client(&euicc_ctx)) { error_function_name = "es9p_authenticate_client"; error_detail = strdup(euicc_ctx.http.status.message); goto err; } // preview here if (euicc_ctx.http._internal.prepare_download_param->b64_profileMetadata) { CANCELPOINT(); if (es8p_metadata_parse(&profile_metadata, euicc_ctx.http._internal.prepare_download_param->b64_profileMetadata)) { error_function_name = "es8p_meatadata_parse"; error_detail = NULL; goto err; } jmetadata = cJSON_CreateObject(); cJSON_AddStringOrNullToObject(jmetadata, "iccid", profile_metadata->iccid); cJSON_AddStringOrNullToObject(jmetadata, "serviceProviderName", profile_metadata->serviceProviderName); cJSON_AddStringOrNullToObject(jmetadata, "profileName", profile_metadata->profileName); cJSON_AddStringOrNullToObject(jmetadata, "iconType", euicc_icontype2str(profile_metadata->iconType)); cJSON_AddStringOrNullToObject(jmetadata, "icon", profile_metadata->icon); cJSON_AddStringOrNullToObject(jmetadata, "profileClass", euicc_profileclass2str(profile_metadata->profileClass)); jprint_progress_obj("es8p_meatadata_parse", jmetadata); if (interactive_preview) { char c; jprint_progress("preview", "y/n"); c = getchar(); if (c != 'y' && c != 'Y') { cancelled = 1; } } } CANCELPOINT(); jprint_progress("es10b_prepare_download", smdp); if (es10b_prepare_download(&euicc_ctx, confirmation_code)) { error_function_name = "es10b_prepare_download"; error_detail = NULL; goto err; } CANCELPOINT(); jprint_progress("es9p_get_bound_profile_package", smdp); if (es9p_get_bound_profile_package(&euicc_ctx)) { error_function_name = "es9p_get_bound_profile_package"; error_detail = strdup(euicc_ctx.http.status.message); goto err; } CANCELPOINT(); jprint_progress("es10b_load_bound_profile_package", smdp); if (es10b_load_bound_profile_package(&euicc_ctx, &download_result)) { jprint_progress_obj("es10b_load_bound_profile_package:result", build_download_result_json(&download_result)); char buffer[256]; snprintf(buffer, sizeof(buffer), "%s,%s", euicc_bppcommandid2str(download_result.bppCommandId), euicc_errorreason2str(download_result.errorReason)); error_function_name = "es10b_load_bound_profile_package"; error_detail = strdup(buffer); goto err; } jprint_success(build_download_result_json(&download_result)); fret = 0; goto exit; err: fret = -1; jprint_progress("es10b_cancel_session", smdp); es10b_cancel_session(&euicc_ctx, ES10B_CANCEL_SESSION_REASON_ENDUSERREJECTION); jprint_progress("es9p_cancel_session", smdp); es9p_cancel_session(&euicc_ctx); if (!cancelled) { jprint_error(error_function_name, error_detail); } else { jprint_error("cancelled", NULL); } exit: euicc_http_cleanup(&euicc_ctx); return fret; } struct applet_entry applet_profile_download = { .name = "download", .main = applet_main, }; estkme-group-lpac-c2fcf5e/src/applet/profile/download.h000066400000000000000000000001271504765665400233460ustar00rootroot00000000000000#pragma once #include extern struct applet_entry applet_profile_download; estkme-group-lpac-c2fcf5e/src/applet/profile/enable.c000066400000000000000000000025541504765665400227660ustar00rootroot00000000000000#include "enable.h" #include "main.h" #include #include #include #include #include static int applet_main(int argc, char **argv) { int ret; const char *param; int refreshflag; if (argc < 2) { printf("Usage: %s [iccid/aid] [refreshflag]\n", argv[0]); printf("\t[refreshflag]: optional\n"); return -1; } param = argv[1]; refreshflag = 0; if (argc > 2) { refreshflag = atoi(argv[2]); } ret = es10c_enable_profile(&euicc_ctx, param, refreshflag); if (ret) { const char *reason; switch (ret) { case 1: reason = "iccid or aid not found"; break; case 2: reason = "profile not in disabled state"; break; case 3: reason = "disallowed by policy"; break; case 4: reason = "wrong profile reenabling"; break; case -1: reason = "internal error, maybe illegal iccid/aid coding"; break; default: reason = "unknown"; break; } jprint_error("es10c_enable_profile", reason); return -1; } jprint_success(NULL); return 0; } struct applet_entry applet_profile_enable = { .name = "enable", .main = applet_main, }; estkme-group-lpac-c2fcf5e/src/applet/profile/enable.h000066400000000000000000000001251504765665400227630ustar00rootroot00000000000000#pragma once #include extern struct applet_entry applet_profile_enable; estkme-group-lpac-c2fcf5e/src/applet/profile/list.c000066400000000000000000000032411504765665400225050ustar00rootroot00000000000000#include "list.h" #include "main.h" #include #include #include #include #include #include #include static int applet_main(int argc, char **argv) { _cleanup_es10c_profile_info_list_ struct es10c_profile_info_list *profiles; struct es10c_profile_info_list *rptr; cJSON *jdata = NULL; if (es10c_get_profiles_info(&euicc_ctx, &profiles)) { jprint_error("es10c_get_profiles_info", NULL); return -1; } jdata = cJSON_CreateArray(); rptr = profiles; while (rptr) { cJSON *jprofile = NULL; jprofile = cJSON_CreateObject(); cJSON_AddStringOrNullToObject(jprofile, "iccid", rptr->iccid); cJSON_AddStringOrNullToObject(jprofile, "isdpAid", rptr->isdpAid); cJSON_AddStringOrNullToObject(jprofile, "profileState", euicc_profilestate2str(rptr->profileState)); cJSON_AddStringOrNullToObject(jprofile, "profileNickname", rptr->profileNickname); cJSON_AddStringOrNullToObject(jprofile, "serviceProviderName", rptr->serviceProviderName); cJSON_AddStringOrNullToObject(jprofile, "profileName", rptr->profileName); cJSON_AddStringOrNullToObject(jprofile, "iconType", euicc_icontype2str(rptr->iconType)); cJSON_AddStringOrNullToObject(jprofile, "icon", rptr->icon); cJSON_AddStringOrNullToObject(jprofile, "profileClass", euicc_profileclass2str(rptr->profileClass)); cJSON_AddItemToArray(jdata, jprofile); rptr = rptr->next; } jprint_success(jdata); return 0; } struct applet_entry applet_profile_list = { .name = "list", .main = applet_main, }; estkme-group-lpac-c2fcf5e/src/applet/profile/list.h000066400000000000000000000001231504765665400225060ustar00rootroot00000000000000#pragma once #include extern struct applet_entry applet_profile_list; estkme-group-lpac-c2fcf5e/src/applet/profile/nickname.c000066400000000000000000000017641504765665400233270ustar00rootroot00000000000000#include "nickname.h" #include "main.h" #include #include #include #include #include static int applet_main(int argc, char **argv) { int ret; const char *iccid; const char *new_name; if (argc < 2) { printf("Usage: %s [iccid] [new_name]\n", argv[0]); printf("\t[new_name]: optional\n"); return -1; } iccid = argv[1]; if (argc > 2) { new_name = argv[2]; } else { new_name = ""; } if ((ret = es10c_set_nickname(&euicc_ctx, iccid, new_name))) { const char *reason; switch (ret) { case 1: reason = "iccid not found"; break; default: reason = "unknown"; break; } jprint_error("es10c_set_nickname", reason); return -1; } jprint_success(NULL); return 0; } struct applet_entry applet_profile_nickname = { .name = "nickname", .main = applet_main, }; estkme-group-lpac-c2fcf5e/src/applet/profile/nickname.h000066400000000000000000000001271504765665400233240ustar00rootroot00000000000000#pragma once #include extern struct applet_entry applet_profile_nickname; estkme-group-lpac-c2fcf5e/src/applet/version.c000066400000000000000000000005001504765665400215520ustar00rootroot00000000000000#include "version.h" #include "main.h" #ifndef LPAC_VERSION # define LPAC_VERSION "v0.0.0-unknown" #endif static int applet_main(int argc, char **argv) { jprint_success(cJSON_CreateString(LPAC_VERSION)); return 0; } struct applet_entry applet_version = { .name = "version", .main = applet_main, }; estkme-group-lpac-c2fcf5e/src/applet/version.h000066400000000000000000000001441504765665400215630ustar00rootroot00000000000000#pragma once #include "version.h" #include extern struct applet_entry applet_version; estkme-group-lpac-c2fcf5e/src/jprint.c000066400000000000000000000054431504765665400201210ustar00rootroot00000000000000#include "jprint.h" #include #include #include #include #include void jprint_error(const char *function_name, const char *detail) { _cleanup_cjson_ cJSON *jroot = NULL; cJSON *jpayload = NULL; _cleanup_free_ char *jstr = NULL; if (detail == NULL) { detail = ""; } jroot = cJSON_CreateObject(); cJSON_AddStringOrNullToObject(jroot, "type", "lpa"); jpayload = cJSON_CreateObject(); cJSON_AddNumberToObject(jpayload, "code", -1); cJSON_AddStringOrNullToObject(jpayload, "message", function_name); cJSON_AddStringOrNullToObject(jpayload, "data", detail); cJSON_AddItemToObject(jroot, "payload", jpayload); jstr = cJSON_PrintUnformatted(jroot); printf("%s\n", jstr); fflush(stdout); } void jprint_progress(const char *function_name, const char *detail) { _cleanup_cjson_ cJSON *jroot = NULL; cJSON *jpayload = NULL; _cleanup_free_ char *jstr = NULL; jroot = cJSON_CreateObject(); cJSON_AddStringOrNullToObject(jroot, "type", "progress"); jpayload = cJSON_CreateObject(); cJSON_AddNumberToObject(jpayload, "code", 0); cJSON_AddStringOrNullToObject(jpayload, "message", function_name); cJSON_AddStringOrNullToObject(jpayload, "data", detail); cJSON_AddItemToObject(jroot, "payload", jpayload); jstr = cJSON_PrintUnformatted(jroot); printf("%s\n", jstr); fflush(stdout); } void jprint_progress_obj(const char *function_name, cJSON *jdata) { _cleanup_cjson_ cJSON *jroot = NULL; cJSON *jpayload = NULL; _cleanup_free_ char *jstr = NULL; jroot = cJSON_CreateObject(); cJSON_AddStringOrNullToObject(jroot, "type", "progress"); jpayload = cJSON_CreateObject(); cJSON_AddNumberToObject(jpayload, "code", 0); cJSON_AddStringOrNullToObject(jpayload, "message", function_name); if (jdata) { cJSON_AddItemToObject(jpayload, "data", jdata); } else { cJSON_AddNullToObject(jpayload, "data"); } cJSON_AddItemToObject(jroot, "payload", jpayload); jstr = cJSON_PrintUnformatted(jroot); printf("%s\n", jstr); fflush(stdout); } void jprint_success(cJSON *jdata) { _cleanup_cjson_ cJSON *jroot = NULL; cJSON *jpayload = NULL; _cleanup_free_ char *jstr = NULL; jroot = cJSON_CreateObject(); cJSON_AddStringOrNullToObject(jroot, "type", "lpa"); jpayload = cJSON_CreateObject(); cJSON_AddNumberToObject(jpayload, "code", 0); cJSON_AddStringOrNullToObject(jpayload, "message", "success"); if (jdata) { cJSON_AddItemToObject(jpayload, "data", jdata); } else { cJSON_AddNullToObject(jpayload, "data"); } cJSON_AddItemToObject(jroot, "payload", jpayload); jstr = cJSON_PrintUnformatted(jroot); printf("%s\n", jstr); fflush(stdout); } estkme-group-lpac-c2fcf5e/src/jprint.h000066400000000000000000000004301504765665400201150ustar00rootroot00000000000000#pragma once #include void jprint_error(const char *function_name, const char *detail); void jprint_progress(const char *function_name, const char *detail); void jprint_progress_obj(const char *function_name, cJSON *jdata); void jprint_success(cJSON *jdata); estkme-group-lpac-c2fcf5e/src/main.c000066400000000000000000000104071504765665400175330ustar00rootroot00000000000000#include "main.h" #include "applet.h" #include "applet/chip.h" #include "applet/notification.h" #include "applet/profile.h" #include "applet/version.h" #include #include #include #include #include #include #include #include #ifdef WIN32 # include // windef.h MUST before other Windows headers # include # include # include # include #endif #define ENV_ISD_R_AID "LPAC_CUSTOM_ISD_R_AID" #define ISD_R_AID_MAX_LENGTH 16 #define ENV_ES10X_MSS "LPAC_CUSTOM_ES10X_MSS" #define ES10X_MSS_MIN_VALUE 6 #define ES10X_MSS_MAX_VALUE 255 #define ENV_APDU_DRIVER "LPAC_APDU" #define ENV_HTTP_DRIVER "LPAC_HTTP" static int driver_applet_main(const int argc, char **argv) { const struct applet_entry *applets[] = { &(struct applet_entry){ .name = "apdu", .main = euicc_driver_main_apdu, }, &(struct applet_entry){ .name = "http", .main = euicc_driver_main_http, }, &(struct applet_entry){ .name = "list", .main = euicc_driver_list, }, NULL, }; return applet_entry(argc, argv, applets); } struct applet_entry driver_applet = { .name = "driver", .main = driver_applet_main, }; static const struct applet_entry *applets[] = { &driver_applet, &applet_chip, &applet_profile, &applet_notification, &applet_version, NULL, }; static int euicc_ctx_inited = 0; struct euicc_ctx euicc_ctx = {0}; static int setup_aid(const uint8_t **aid, uint8_t *aid_len) { *aid = NULL; *aid_len = 0; const char *value = getenv(ENV_ISD_R_AID); if (value == NULL) return 0; uint8_t *parsed = malloc(ISD_R_AID_MAX_LENGTH); const int n = euicc_hexutil_hex2bin(parsed, ISD_R_AID_MAX_LENGTH, value); if (n < 1) return -1; *aid = parsed; *aid_len = n; return 0; } static int setup_mss(uint8_t *mss) { *mss = 0; const char *value = getenv(ENV_ES10X_MSS); if (value == NULL) return 0; const long parsed = strtol(value, NULL, 10); if (parsed == 0) return 0; if (parsed < ES10X_MSS_MIN_VALUE) return -1; if (parsed > ES10X_MSS_MAX_VALUE) return -1; *mss = (uint8_t)parsed; return 0; } int main_init_euicc() { if (setup_aid(&euicc_ctx.aid, &euicc_ctx.aid_len)) { jprint_error("euicc_init", "invalid custom ISD-R applet id given"); return -1; } if (setup_mss(&euicc_ctx.es10x_mss)) { jprint_error("euicc_init", "invalid custom ES10x MSS given"); return -1; } if (euicc_init(&euicc_ctx)) { jprint_error("euicc_init", NULL); return -1; } euicc_ctx_inited = 1; return 0; } void main_fini_euicc() { if (!euicc_ctx_inited) { return; } euicc_fini(&euicc_ctx); euicc_ctx_inited = 0; } #ifdef WIN32 static char **warg_to_arg(const int wargc, wchar_t **wargv) { char **argv = malloc(wargc * sizeof(char *)); if (argv == NULL) { return NULL; } for (int i = 0; i < wargc; ++i) { const int size = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, NULL, 0, NULL, NULL); argv[i] = malloc(size); if (argv[i] == NULL) { for (int j = 0; j < i; ++j) { free(argv[j]); } free(argv); return NULL; } WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i], size, NULL, NULL); } return argv; } #endif int main(int argc, char **argv) { int ret = 0; setlocale(LC_ALL, "C.UTF-8"); memset(&euicc_ctx, 0, sizeof(euicc_ctx)); const char *apdu_driver = getenv(ENV_APDU_DRIVER); const char *http_driver = getenv(ENV_HTTP_DRIVER); if (euicc_driver_init(apdu_driver, http_driver)) { return -1; } euicc_ctx.apdu.interface = &euicc_driver_interface_apdu; euicc_ctx.http.interface = &euicc_driver_interface_http; #ifdef WIN32 argv = warg_to_arg(argc, CommandLineToArgvW(GetCommandLineW(), &argc)); if (argv == NULL) { return -1; } #endif ret = applet_entry(argc, argv, applets); main_fini_euicc(); euicc_driver_fini(); return ret; } estkme-group-lpac-c2fcf5e/src/main.h000066400000000000000000000002271504765665400175370ustar00rootroot00000000000000#pragma once #include #include extern struct euicc_ctx euicc_ctx; int main_init_euicc(void); void main_fini_euicc(void); estkme-group-lpac-c2fcf5e/src/version.h.in000066400000000000000000000001631504765665400207040ustar00rootroot00000000000000#ifndef LPAC_VERSION_H_ #define LPAC_VERSION_H_ #define LPAC_VERSION "@LPAC_VERSION@" #endif /* LPAC_VERSION_H_ */ estkme-group-lpac-c2fcf5e/utils/000077500000000000000000000000001504765665400170125ustar00rootroot00000000000000estkme-group-lpac-c2fcf5e/utils/CMakeLists.txt000066400000000000000000000002631504765665400215530ustar00rootroot00000000000000add_library(lpac-utils OBJECT lpac/utils.c) target_include_directories(lpac-utils PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(lpac-utils PRIVATE cjson-static euicc) estkme-group-lpac-c2fcf5e/utils/lpac/000077500000000000000000000000001504765665400177315ustar00rootroot00000000000000estkme-group-lpac-c2fcf5e/utils/lpac/utils.c000066400000000000000000000044631504765665400212440ustar00rootroot00000000000000#include "utils.h" #include #include #include #include #include static bool is_numeric(const char *value) { if (value == NULL) return false; for (size_t i = strlen(value); i > 0; --i) { if (isdigit(value[i])) continue; return false; } return true; } const char *getenv_str_or_default(const char *name, const char *default_value) { const char *value = getenv(name); if (value == NULL) return default_value; return value; } bool getenv_bool_or_default(const char *name, const bool default_value) { const char *value = getenv(name); if (value == NULL) return default_value; if (is_numeric(value)) return strcmp(value, "0") != 0; return strcasecmp(value, "y") == 0 || strcasecmp(value, "yes") == 0 || strcasecmp(value, "true") == 0; } int getenv_int_or_default(const char *name, const int default_value) { return (int)getenv_long_or_default(name, default_value); } long getenv_long_or_default(const char *name, const long default_value) { const char *value = getenv(name); if (value == NULL) return default_value; return strtol(value, NULL, 10); } void set_deprecated_env_name(const char *name, const char *deprecated_name) { const char *value = getenv(name); if (value != NULL) return; // new env var already set value = getenv(deprecated_name); if (value == NULL) return; // deprecated env var not set fprintf(stderr, "WARNING: Please use '%s' instead of '%s'\n", name, deprecated_name); #ifdef _WIN32 _putenv_s(name, value); #else setenv(name, value, 1); #endif } bool json_print(char *type, cJSON *jpayload) { _cleanup_cjson_ cJSON *jroot = NULL; _cleanup_free_ char *jstr = NULL; if (jpayload == NULL) { goto err; } jroot = cJSON_CreateObject(); if (jroot == NULL) { goto err; } if (cJSON_AddStringOrNullToObject(jroot, "type", type) == NULL) { goto err; } if (cJSON_AddItemReferenceToObject(jroot, "payload", jpayload) == 0) { goto err; } jstr = cJSON_PrintUnformatted(jroot); if (jstr == NULL) { goto err; } fprintf(stdout, "%s\n", jstr); fflush(stdout); return true; err: return false; } estkme-group-lpac-c2fcf5e/utils/lpac/utils.h000066400000000000000000000043051504765665400212440ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #define HTTP_ENV_NAME(DRIVER, NAME) "LPAC_HTTP_" #DRIVER "_" #NAME #define APDU_ENV_NAME(DRIVER, NAME) "LPAC_APDU_" #DRIVER "_" #NAME #define _cleanup_(x) __attribute__((cleanup(x))) #define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \ static inline void func##p(type *p) { \ if (*p) \ func(*p); \ } \ struct __useless_struct_to_allow_trailing_semicolon__ DEFINE_TRIVIAL_CLEANUP_FUNC(cJSON *, cJSON_Delete); #define _cleanup_cjson_ _cleanup_(cJSON_Deletep) DEFINE_TRIVIAL_CLEANUP_FUNC(struct es10b_notification_metadata_list *, es10b_notification_metadata_list_free_all); #define _cleanup_es10b_notification_metadata_list_ _cleanup_(es10b_notification_metadata_list_free_allp) DEFINE_TRIVIAL_CLEANUP_FUNC(struct es10b_rat *, es10b_rat_list_free_all); #define _cleanup_es10b_rat_list_ _cleanup_(es10b_rat_list_free_allp) DEFINE_TRIVIAL_CLEANUP_FUNC(struct es10c_profile_info_list *, es10c_profile_info_list_free_all); #define _cleanup_es10c_profile_info_list_ _cleanup_(es10c_profile_info_list_free_allp) DEFINE_TRIVIAL_CLEANUP_FUNC(char **, es11_smdp_list_free_all); #define _cleanup_es11_smdp_list_ _cleanup_(es11_smdp_list_free_allp) static inline void freep(void *p) { free(*(void **)p); } #define _cleanup_free_ _cleanup_(freep) #define getenv_or_default(name, default_value) \ _Generic((default_value), \ bool: getenv_bool_or_default, \ int: getenv_int_or_default, \ long: getenv_long_or_default, \ char *: getenv_str_or_default)(name, default_value) const char *getenv_str_or_default(const char *name, const char *default_value); bool getenv_bool_or_default(const char *name, bool default_value); int getenv_int_or_default(const char *name, int default_value); long getenv_long_or_default(const char *name, long default_value); void set_deprecated_env_name(const char *name, const char *deprecated_name); bool json_print(char *type, cJSON *jpayload);